aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-15 23:42:28 +0900
committernsfisis <nsfisis@gmail.com>2026-05-16 10:00:40 +0900
commit7421f3019987a642d1c79d4dd36c9e10f0c7da03 (patch)
tree2bc54c5b2cb6dcce016ee2efb173f9d002196d36 /crates/shirabe/src
parentd6ee7d49506a87c34aa5b407932d414a552a4523 (diff)
downloadphp-shirabe-7421f3019987a642d1c79d4dd36c9e10f0c7da03.tar.gz
php-shirabe-7421f3019987a642d1c79d4dd36c9e10f0c7da03.tar.zst
php-shirabe-7421f3019987a642d1c79d4dd36c9e10f0c7da03.zip
feat(port): port GlobalCommand.php
Diffstat (limited to 'crates/shirabe/src')
-rw-r--r--crates/shirabe/src/command/global_command.rs125
1 files changed, 125 insertions, 0 deletions
diff --git a/crates/shirabe/src/command/global_command.rs b/crates/shirabe/src/command/global_command.rs
index 54b1149..5dc22a3 100644
--- a/crates/shirabe/src/command/global_command.rs
+++ b/crates/shirabe/src/command/global_command.rs
@@ -1 +1,126 @@
//! ref: composer/src/Composer/Command/GlobalCommand.php
+
+use std::path::Path;
+
+use anyhow::Result;
+use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput;
+use shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions;
+use shirabe_external_packages::symfony::console::input::input_interface::InputInterface;
+use shirabe_external_packages::symfony::console::input::string_input::StringInput;
+use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface;
+use shirabe_php_shim::{chdir, LogicException, RuntimeException};
+
+use crate::command::base_command::BaseCommand;
+use crate::console::input::input_argument::InputArgument;
+use crate::factory::Factory;
+use crate::util::filesystem::Filesystem;
+use crate::util::platform::Platform;
+
+#[derive(Debug)]
+pub struct GlobalCommand {
+ inner: BaseCommand,
+}
+
+impl GlobalCommand {
+ pub fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions) {
+ let application = self.inner.get_application();
+ if input.must_suggest_argument_values_for("command-name") {
+ let names: Vec<String> = application.all()
+ .into_iter()
+ .filter(|cmd| !cmd.is_hidden())
+ .filter_map(|cmd| cmd.get_name().map(|n| n.to_string()))
+ .collect();
+ suggestions.suggest_values(names);
+ return;
+ }
+
+ let command_name = input.get_argument("command-name").as_string().unwrap_or("").to_string();
+ if application.has(&command_name) {
+ let sub_input = self.prepare_subcommand_input(input.as_input_interface(), true);
+ let sub_input = CompletionInput::from_string(&sub_input.to_string(), 2);
+ let command = application.find(&command_name);
+ command.merge_application_definition();
+ sub_input.bind(command.get_definition());
+ command.complete(&sub_input, suggestions);
+ }
+ }
+
+ pub fn configure(&mut self) {
+ self.inner
+ .set_name("global")
+ .set_description("Allows running commands in the global composer dir ($COMPOSER_HOME)")
+ .set_definition(vec![
+ InputArgument::new("command-name", Some(InputArgument::REQUIRED), "", None, vec![]),
+ InputArgument::new("args", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "", None, vec![]),
+ ])
+ .set_help(
+ "Use this command as a wrapper to run other Composer commands\n\
+ within the global context of COMPOSER_HOME.\n\n\
+ You can use this to install CLI utilities globally, all you need\n\
+ is to add the COMPOSER_HOME/vendor/bin dir to your PATH env var.\n\n\
+ COMPOSER_HOME is c:\\Users\\<user>\\AppData\\Roaming\\Composer on Windows\n\
+ and /home/<user>/.composer on unix systems.\n\n\
+ If your system uses freedesktop.org standards, then it will first check\n\
+ XDG_CONFIG_HOME or default to /home/<user>/.config/composer\n\n\
+ Note: This path may vary depending on customizations to bin-dir in\n\
+ composer.json or the environmental variable COMPOSER_BIN_DIR.\n\n\
+ Read more at https://getcomposer.org/doc/03-cli.md#global"
+ );
+ }
+
+ pub fn run(&self, input: &dyn InputInterface, output: &dyn OutputInterface) -> Result<i64> {
+ let tokens = Preg::split(r"{\s+}", &input.to_string())?;
+ let mut args: Vec<String> = vec![];
+ for token in &tokens {
+ if !token.is_empty() && !token.starts_with('-') {
+ args.push(token.clone());
+ if args.len() >= 2 {
+ break;
+ }
+ }
+ }
+
+ if args.len() < 2 {
+ return self.inner.run(input, output);
+ }
+
+ let sub_input = self.prepare_subcommand_input(input, false)?;
+ Ok(self.inner.get_application().run(&sub_input, output)?)
+ }
+
+ fn prepare_subcommand_input(&self, input: &dyn InputInterface, quiet: bool) -> Result<StringInput> {
+ if Platform::get_env("COMPOSER").is_some() {
+ Platform::clear_env("COMPOSER");
+ }
+
+ let config = Factory::create_config(None, None)?;
+ let home = config.get("home").as_string().unwrap_or("").to_string();
+
+ if !Path::new(&home).is_dir() {
+ let fs = Filesystem::new();
+ fs.ensure_directory_exists(&home)?;
+ if !Path::new(&home).is_dir() {
+ return Err(RuntimeException {
+ message: "Could not create home directory".to_string(),
+ code: 0,
+ }.into());
+ }
+ }
+
+ chdir(&home).map_err(|e| RuntimeException { message: format!("Could not switch to home directory \"{}\"", home), code: 0 })?;
+
+ if !quiet {
+ self.inner.get_io().write_error(&format!("<info>Changed current directory to {}</info>", home));
+ }
+
+ let new_input_str = Preg::replace(r"{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}", "", &input.to_string(), 1)?;
+ self.inner.get_application().reset_composer();
+
+ Ok(StringInput::new(new_input_str))
+ }
+
+ pub fn is_proxy_command(&self) -> bool {
+ true
+ }
+}