diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-19 21:46:01 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-19 21:46:08 +0900 |
| commit | 5e31fa33c3b5cf726a57a063b8e7a070869250fe (patch) | |
| tree | 98522466966fa7df483cad174ab5fc03db39bc09 /crates/shirabe/src/command/config_command.rs | |
| parent | c839244d8d09f3036ebfee8eef7eb6b147e593ab (diff) | |
| download | php-shirabe-5e31fa33c3b5cf726a57a063b8e7a070869250fe.tar.gz php-shirabe-5e31fa33c3b5cf726a57a063b8e7a070869250fe.tar.zst php-shirabe-5e31fa33c3b5cf726a57a063b8e7a070869250fe.zip | |
fix(compile): fix more random compile errors
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/command/config_command.rs')
| -rw-r--r-- | crates/shirabe/src/command/config_command.rs | 222 |
1 files changed, 115 insertions, 107 deletions
diff --git a/crates/shirabe/src/command/config_command.rs b/crates/shirabe/src/command/config_command.rs index d4f64f0..bef9d47 100644 --- a/crates/shirabe/src/command/config_command.rs +++ b/crates/shirabe/src/command/config_command.rs @@ -3,9 +3,9 @@ use crate::io::io_interface; use indexmap::IndexMap; -use shirabe_external_packages::composer::pcre::preg::Preg; +use crate::console::input::input_option::InputOption; +use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg}; use shirabe_external_packages::symfony::component::console::input::input_interface::InputInterface; -use shirabe_external_packages::symfony::component::console::input::input_option::InputOption; use shirabe_external_packages::symfony::component::console::output::output_interface::OutputInterface; use shirabe_php_shim::{ ArrayObject, InvalidArgumentException, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, @@ -37,7 +37,7 @@ use shirabe_semver::version_parser::VersionParser; pub struct ConfigCommand { base_command_data: BaseCommandData, - config: Option<Config>, + config: Option<std::rc::Rc<std::cell::RefCell<Config>>>, config_file: Option<JsonFile>, config_source: Option<JsonConfigSource>, @@ -64,24 +64,22 @@ impl ConfigCommand { pub(crate) fn configure(&mut self) { // TODO(cli-completion): suggest_setting_keys() for `setting-key` argument - self - .inner - .set_name("config") + self.set_name("config") .set_description("Sets config options") - .set_definition(vec![ - InputOption::new("global", Some("g"), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None), - InputOption::new("editor", Some("e"), Some(InputOption::VALUE_NONE), "Open editor", None), - InputOption::new("auth", Some("a"), Some(InputOption::VALUE_NONE), "Affect auth config file (only used for --editor)", None), - InputOption::new("unset", None, Some(InputOption::VALUE_NONE), "Unset the given setting-key", None), - InputOption::new("list", Some("l"), Some(InputOption::VALUE_NONE), "List configuration settings", None), - InputOption::new("file", Some("f"), Some(InputOption::VALUE_REQUIRED), "If you want to choose a different composer.json or config.json", None), - InputOption::new("absolute", None, Some(InputOption::VALUE_NONE), "Returns absolute paths when fetching *-dir config values instead of relative", None), - InputOption::new("json", Some("j"), Some(InputOption::VALUE_NONE), "JSON decode the setting value, to be used with extra.* keys", None), - InputOption::new("merge", Some("m"), Some(InputOption::VALUE_NONE), "Merge the setting value with the current value, to be used with extra.* or audit.ignore[-abandoned] keys in combination with --json", None), - InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority)", None), - InputOption::new("source", None, Some(InputOption::VALUE_NONE), "Display where the config value is loaded from", None), - InputArgument::new("setting-key", None, "Setting key", None), - InputArgument::new("setting-value", Some(InputArgument::IS_ARRAY), "Setting value", None), + .set_definition(&[ + InputOption::new("global", Some(PhpMixed::String("g".to_string())), Some(InputOption::VALUE_NONE), "Apply command to the global config file", None).unwrap().into(), + InputOption::new("editor", Some(PhpMixed::String("e".to_string())), Some(InputOption::VALUE_NONE), "Open editor", None).unwrap().into(), + InputOption::new("auth", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Affect auth config file (only used for --editor)", None).unwrap().into(), + InputOption::new("unset", None, Some(InputOption::VALUE_NONE), "Unset the given setting-key", None).unwrap().into(), + InputOption::new("list", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_NONE), "List configuration settings", None).unwrap().into(), + InputOption::new("file", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "If you want to choose a different composer.json or config.json", None).unwrap().into(), + InputOption::new("absolute", None, Some(InputOption::VALUE_NONE), "Returns absolute paths when fetching *-dir config values instead of relative", None).unwrap().into(), + InputOption::new("json", Some(PhpMixed::String("j".to_string())), Some(InputOption::VALUE_NONE), "JSON decode the setting value, to be used with extra.* keys", None).unwrap().into(), + InputOption::new("merge", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "Merge the setting value with the current value, to be used with extra.* or audit.ignore[-abandoned] keys in combination with --json", None).unwrap().into(), + InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority)", None).unwrap().into(), + InputOption::new("source", None, Some(InputOption::VALUE_NONE), "Display where the config value is loaded from", None).unwrap().into(), + InputArgument::new("setting-key", None, "Setting key", None).unwrap().into(), + InputArgument::new("setting-value", Some(InputArgument::IS_ARRAY), "Setting value", None).unwrap().into(), ]) .set_help( "This command allows you to edit composer config settings and repositories\n\ @@ -127,15 +125,17 @@ impl ConfigCommand { ) -> anyhow::Result<()> { self.initialize(input, output)?; - let auth_config_file = self - .inner - .get_auth_config_file(input, self.config.as_ref().unwrap()); + let auth_config_file = + self.get_auth_config_file(input, &*self.config.as_ref().unwrap().borrow()); - self.auth_config_file = Some(JsonFile::new(auth_config_file, None, Some(self.get_io()))?); - self.auth_config_source = Some(JsonConfigSource::new_with_auth( - self.auth_config_file.as_ref().unwrap(), - true, - )); + self.auth_config_file = Some(JsonFile::new( + auth_config_file, + None, + Some(self.get_io().clone_box()), + )?); + // TODO(phase-b): JsonConfigSource::new takes owned JsonFile (PHP sharing semantics). + // Skipping auth_config_source assignment until Rc<RefCell<JsonFile>> refactor lands. + self.auth_config_source = None; // Initialize the global file if it's not there, ignoring any warnings or notices if input.get_option("global").as_bool() == Some(true) @@ -188,7 +188,10 @@ impl ConfigCommand { editor = Some("notepad".to_string()); } else { for candidate in &["editor", "vim", "vi", "nano", "pico", "ed"] { - if !exec(&format!("which {}", candidate)).is_empty() { + if !exec(&format!("which {}", candidate), None, None) + .unwrap_or_default() + .is_empty() + { editor = Some(candidate.to_string()); break; } @@ -207,22 +210,25 @@ impl ConfigCommand { } else { self.config_file.as_ref().unwrap().get_path().to_string() }; - system(&format!( - "{} {}{}", - editor.unwrap_or_default(), - file, - if Platform::is_windows() { - "" - } else { - " > `tty`" - } - )); + system( + &format!( + "{} {}{}", + editor.unwrap_or_default(), + file, + if Platform::is_windows() { + "" + } else { + " > `tty`" + } + ), + None, + ); return Ok(0); } if input.get_option("global").as_bool() != Some(true) { - self.config.as_mut().unwrap().merge( + self.config.as_mut().unwrap().borrow_mut().merge( self.config_file.as_ref().unwrap().read()?, self.config_file.as_ref().unwrap().get_path(), ); @@ -233,21 +239,20 @@ impl ConfigCommand { }; let mut wrap: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); wrap.insert("config".to_string(), Box::new(auth_data)); - self.config.as_mut().unwrap().merge( + self.config.as_mut().unwrap().borrow_mut().merge( PhpMixed::Array(wrap), self.auth_config_file.as_ref().unwrap().get_path(), ); } - self.inner - .get_io() - .load_configuration(self.config.as_ref().unwrap()); + self.get_io() + .load_configuration(&mut *self.config.as_ref().unwrap().borrow_mut())?; // List the configuration of the file settings if input.get_option("list").as_bool() == Some(true) { self.list_configuration( - self.config.as_ref().unwrap().all(), - self.config.as_ref().unwrap().raw(), + self.config.as_ref().unwrap().borrow_mut().all(0)?, + self.config.as_ref().unwrap().borrow().raw(), output, None, input.get_option("source").as_bool() == Some(true), @@ -297,31 +302,33 @@ impl ConfigCommand { properties_defaults.insert("suggest".to_string(), PhpMixed::List(vec![])); properties_defaults.insert("extra".to_string(), PhpMixed::List(vec![])); let raw_data = self.config_file.as_ref().unwrap().read()?; - let mut data = self.config.as_ref().unwrap().all(); + let mut data = self.config.as_ref().unwrap().borrow_mut().all(0)?; let mut source = self - .inner .config .as_ref() .unwrap() .get_source_of_value(&setting_key); let mut value: PhpMixed; - let mut matches: Vec<String> = vec![]; - if Preg::is_match( + let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); + if Preg::is_match3( "/^repos?(?:itories)?(?:\\.(.+))?/", &setting_key, Some(&mut matches), ) .unwrap_or(false) { - if matches.get(1).is_none() { + if matches.get(&CaptureKey::ByIndex(1)).is_none() { value = data .as_array() .and_then(|a| a.get("repositories")) .map(|v| (**v).clone()) .unwrap_or_else(|| PhpMixed::Array(IndexMap::new())); } else { - let repo_key = matches[1].clone(); + let repo_key = matches + .get(&CaptureKey::ByIndex(1)) + .cloned() + .unwrap_or_default(); let repos = data .as_array() .and_then(|a| a.get("repositories")) @@ -385,7 +392,7 @@ impl ConfigCommand { .map(|c| c.contains_key(&setting_key)) .unwrap_or(false) { - value = self.config.as_ref().unwrap().get_with_flags( + value = self.config.as_ref().unwrap().borrow_mut().get_with_flags( &setting_key, if input.get_option("absolute").as_bool() == Some(true) { 0 @@ -396,8 +403,10 @@ impl ConfigCommand { // ensure we get {} output for properties which are objects if value.as_array().map(|a| a.is_empty()).unwrap_or(false) { let schema = JsonFile::parse_json( - &file_get_contents(JsonFile::COMPOSER_SCHEMA_PATH).unwrap_or_default(), - "composer.schema.json", + Some( + &file_get_contents(JsonFile::COMPOSER_SCHEMA_PATH).unwrap_or_default(), + ), + Some("composer.schema.json"), )?; let type_value = schema .as_array() @@ -439,13 +448,7 @@ impl ConfigCommand { && in_array(setting_key.as_str(), &properties, true) { value = (**raw_data.as_array().unwrap().get(&setting_key).unwrap()).clone(); - source = self - .inner - .config_file - .as_ref() - .unwrap() - .get_path() - .to_string(); + source = self.config_file.as_ref().unwrap().get_path().to_string(); } else if let Some(v) = properties_defaults.get(&setting_key) { value = v.clone(); source = "defaults".to_string(); @@ -458,7 +461,7 @@ impl ConfigCommand { } let value_str = if is_array(&value) || is_object(&value) || is_bool(&value) { - JsonFile::encode(&value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)? + JsonFile::encode(&value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) } else { value.as_string().unwrap_or("").to_string() }; @@ -468,7 +471,7 @@ impl ConfigCommand { source_of_config_value = format!(" ({})", source); } - self.get_io().write( + self.get_io().write3( &format!("{}{}", value_str, source_of_config_value), true, io_interface::QUIET, @@ -516,7 +519,6 @@ impl ConfigCommand { { if setting_key == "disable-tls" && self - .inner .config .as_ref() .unwrap() @@ -547,8 +549,8 @@ impl ConfigCommand { return Ok(0); } // handle preferred-install per-package config - let mut matches: Vec<String> = vec![]; - if Preg::is_match( + let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); + if Preg::is_match3( "/^preferred-install\\.(.+)/", &setting_key, Some(&mut matches), @@ -588,8 +590,8 @@ impl ConfigCommand { } // handle allow-plugins config setting elements true or false to add/remove - let mut matches: Vec<String> = vec![]; - if Preg::is_match( + let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); + if Preg::is_match3( "{^allow-plugins\\.([a-zA-Z0-9/*-]+)}", &setting_key, Some(&mut matches), @@ -660,8 +662,8 @@ impl ConfigCommand { } // handle repositories - let mut matches: Vec<String> = vec![]; - if Preg::is_match_strict_groups( + let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); + if Preg::is_match_strict_groups3( "/^repos?(?:itories)?\\.(.+)/", &setting_key, Some(&mut matches), @@ -712,7 +714,7 @@ impl ConfigCommand { return Ok(0); } } else { - let value = JsonFile::parse_json(&values[0], "composer.json")?; + let value = JsonFile::parse_json(Some(&values[0]), Some("composer.json"))?; self.config_source.as_mut().unwrap().add_repository( &matches[1], value, @@ -731,8 +733,8 @@ impl ConfigCommand { } // handle extra - let mut matches: Vec<String> = vec![]; - if Preg::is_match("/^extra\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { + let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); + if Preg::is_match3("/^extra\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { if input.get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() @@ -744,7 +746,7 @@ impl ConfigCommand { let mut value = PhpMixed::String(values[0].clone()); if input.get_option("json").as_bool() == Some(true) { - value = JsonFile::parse_json(&values[0], "composer.json")?; + value = JsonFile::parse_json(Some(&values[0]), Some("composer.json"))?; if input.get_option("merge").as_bool() == Some(true) { let current_value_outer = self.config_file.as_ref().unwrap().read()?; let bits = explode(".", &setting_key); @@ -787,8 +789,8 @@ impl ConfigCommand { } // handle suggest - let mut matches: Vec<String> = vec![]; - if Preg::is_match("/^suggest\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { + let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); + if Preg::is_match3("/^suggest\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { if input.get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() @@ -822,8 +824,9 @@ impl ConfigCommand { } // handle platform - let mut matches: Vec<String> = vec![]; - if Preg::is_match("/^platform\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { + let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); + if Preg::is_match3("/^platform\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) + { if input.get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() @@ -881,7 +884,7 @@ impl ConfigCommand { .collect(), ); if input.get_option("json").as_bool() == Some(true) { - value = JsonFile::parse_json(&values[0], "composer.json")?; + value = JsonFile::parse_json(Some(&values[0]), Some("composer.json"))?; if !is_array(&value) { return Err(RuntimeException { message: format!("Expected an array or object for {}", setting_key), @@ -942,12 +945,8 @@ impl ConfigCommand { } // handle auth - let mut matches: Vec<String> = vec![]; - if Preg::is_match( - "/^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|custom-headers|bearer|forgejo-token)\\.(.+)/", - &setting_key, - Some(&mut matches), - ).unwrap_or(false) { + let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); + if Preg::is_match3("/^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|custom-headers|bearer|forgejo-token)\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { if input.get_option("unset").as_bool() == Some(true) { self.auth_config_source.as_mut().unwrap().remove_config_setting(&format!("{}.{}", matches[1], matches[2])); self.config_source.as_mut().unwrap().remove_config_setting(&format!("{}.{}", matches[1], matches[2])); @@ -1023,8 +1022,8 @@ impl ConfigCommand { } // Check if the header is in correct "Name: Value" format - let mut header_parts: Vec<String> = vec![]; - if !Preg::is_match("/^[^:]+:\\s*.+$/", header, Some(&mut header_parts)).unwrap_or(false) { + let mut header_parts: IndexMap<CaptureKey, String> = IndexMap::new(); + if !Preg::is_match3("/^[^:]+:\\s*.+$/", header, Some(&mut header_parts)).unwrap_or(false) { return Err(RuntimeException { message: format!("Header \"{}\" is not in \"Header-Name: Header-Value\" format", header), code: 0, @@ -1056,8 +1055,8 @@ impl ConfigCommand { } // handle script - let mut matches: Vec<String> = vec![]; - if Preg::is_match("/^scripts\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { + let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); + if Preg::is_match3("/^scripts\\.(.+)/", &setting_key, Some(&mut matches)).unwrap_or(false) { if input.get_option("unset").as_bool() == Some(true) { self.config_source .as_mut() @@ -1143,7 +1142,6 @@ impl ConfigCommand { if key == "disable-tls" { if !normalized_value.as_bool().unwrap_or(false) && self - .inner .config .as_ref() .unwrap() @@ -1156,7 +1154,6 @@ impl ConfigCommand { ); } else if normalized_value.as_bool().unwrap_or(false) && !self - .inner .config .as_ref() .unwrap() @@ -1252,7 +1249,9 @@ impl ConfigCommand { || (key == "repositories" && k.is_none())) { let mut new_k = k.clone().unwrap_or_default(); - new_k.push_str(&Preg::replace("{^config\\.}", "", &format!("{}.", key))); + new_k.push_str( + &Preg::replace("{^config\\.}", "", &format!("{}.", key)).unwrap_or_default(), + ); k = Some(new_k); self.list_configuration(value_inner, raw_val, output, k.clone(), show_source); k = orig_k.clone(); @@ -1285,11 +1284,11 @@ impl ConfigCommand { let source = if show_source { format!( " ({})", - self.config.as_ref().unwrap().get_source_of_value(&format!( - "{}{}", - k.clone().unwrap_or_default(), - key - )) + self.config + .as_ref() + .unwrap() + .borrow_mut() + .get_source_of_value(&format!("{}{}", k.clone().unwrap_or_default(), key)) ) } else { String::new() @@ -1304,13 +1303,14 @@ impl ConfigCommand { } else { k.clone().unwrap() }; - let id = Preg::replace("{\\..*$}", "", &id_source); + let id = Preg::replace("{\\..*$}", "", &id_source).unwrap_or_default(); let id = Preg::replace( "{[^a-z0-9]}i", "-", &strtolower(&shirabe_php_shim::trim(&id, " \t\n\r\0\u{0B}")), - ); - let id = Preg::replace("{-+}", "-", &id); + ) + .unwrap_or_default(); + let id = Preg::replace("{-+}", "-", &id).unwrap_or_default(); link = format!("https://getcomposer.org/doc/06-config.md#{}", id); } if is_string(&raw_val) @@ -1320,7 +1320,7 @@ impl ConfigCommand { .unwrap_or_default() != value_display { - io.write( + io.write3( &format!( "[<fg=yellow;href={}>{}{}</>] <info>{} ({})</info>{}", link, @@ -1334,7 +1334,7 @@ impl ConfigCommand { io_interface::QUIET, ); } else { - io.write( + io.write3( &format!( "[<fg=yellow;href={}>{}{}</>] <info>{}</info>{}", link, @@ -1531,7 +1531,7 @@ fn build_unique_config_values() -> IndexMap<String, (ValidatorFn, NormalizerFn)> ( Box::new(|val| { PhpMixed::Bool( - Preg::is_match( + Preg::is_match3( "/^\\s*([0-9.]+)\\s*(?:([kmg])(?:i?b)?)?\\s*$/i", val.as_string().unwrap_or(""), None, @@ -2008,12 +2008,12 @@ fn key_first_key(value: &PhpMixed) -> Option<String> { } impl BaseConfigCommand for ConfigCommand { - fn config(&self) -> Option<&Config> { + fn config(&self) -> Option<&std::rc::Rc<std::cell::RefCell<Config>>> { self.config.as_ref() } - fn config_mut(&mut self) -> Option<&mut Config> { - self.config.as_mut() + fn config_mut(&mut self) -> &mut Option<std::rc::Rc<std::cell::RefCell<Config>>> { + &mut self.config } fn config_file(&self) -> Option<&JsonFile> { @@ -2024,6 +2024,10 @@ impl BaseConfigCommand for ConfigCommand { self.config_file.as_mut() } + fn set_config_file(&mut self, file: Option<JsonFile>) { + self.config_file = file; + } + fn config_source(&self) -> Option<&JsonConfigSource> { self.config_source.as_ref() } @@ -2031,6 +2035,10 @@ impl BaseConfigCommand for ConfigCommand { fn config_source_mut(&mut self) -> Option<&mut JsonConfigSource> { self.config_source.as_mut() } + + fn set_config_source(&mut self, source: Option<JsonConfigSource>) { + self.config_source = source; + } } impl HasBaseCommandData for ConfigCommand { |
