//! ref: composer/src/Composer/Command/BaseConfigCommand.php use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::config::Config; use crate::config::JsonConfigSource; use crate::factory::Factory; use crate::json::JsonFile; use crate::util::Platform; use crate::util::Silencer; use indexmap::IndexMap; use shirabe_external_packages::symfony::component::console::input::InputInterface; use shirabe_external_packages::symfony::component::console::output::OutputInterface; use shirabe_php_shim::{PhpMixed, chmod, touch}; pub trait BaseConfigCommand: BaseCommand { fn config(&self) -> Option<&std::rc::Rc>>; fn config_mut(&mut self) -> &mut Option>>; fn config_file(&self) -> Option<&JsonFile>; fn config_file_mut(&mut self) -> Option<&mut JsonFile>; fn set_config_file(&mut self, file: Option); fn config_source(&self) -> Option<&JsonConfigSource>; fn config_source_mut(&mut self) -> Option<&mut JsonConfigSource>; fn set_config_source(&mut self, source: Option); fn initialize( &mut self, input: &dyn InputInterface, output: &dyn OutputInterface, ) -> anyhow::Result<()> { // TODO(phase-b): BaseCommand::initialize chained via Self::initialize would recurse; // omitted until trait disambiguation is sorted. if input.get_option("global").as_bool().unwrap_or(false) && !input.get_option("file").is_null() { return Err(anyhow::anyhow!("--file and --global can not be combined")); } // TODO(phase-b): clone_box to release the &mut self borrow held by get_io. let io = self.get_io().clone_box(); *self.config_mut() = Some(std::rc::Rc::new(std::cell::RefCell::new( Factory::create_config(Some(io.as_ref()), None)?, ))); let config_rc = std::rc::Rc::clone(self.config().unwrap()); // When using --global flag, set baseDir to home directory for correct absolute path resolution if input.get_option("global").as_bool().unwrap_or(false) { let home = config_rc.borrow_mut().get("home").to_string(); config_rc.borrow_mut().set_base_dir(Some(home)); } let config_file = self.get_composer_config_file(input, &*config_rc.borrow()); // Create global composer.json if invoked using `composer global [config-cmd]` if (config_file == "composer.json" || config_file == "./composer.json") && !std::path::Path::new(&config_file).exists() && std::fs::canonicalize(Platform::get_cwd(false)?).ok() == std::fs::canonicalize(config_rc.borrow_mut().get("home").to_string()).ok() { std::fs::write(&config_file, "{\n}\n")?; } self.set_config_file(Some(JsonFile::new( config_file.clone(), None, Some(io.clone_box()), )?)); // TODO(phase-b): JsonConfigSource::new takes owned JsonFile, but PHP shares the same // instance with $this->configFile. Needs Rc> refactor on both sides. self.set_config_source(None); // Initialize the global file if it's not there, ignoring any warnings or notices if input.get_option("global").as_bool().unwrap_or(false) && !self.config_file().as_ref().unwrap().exists() { let path = self.config_file().as_ref().unwrap().get_path().to_string(); touch(&path); self.config_file_mut() .as_mut() .unwrap() .write(PhpMixed::Array({ let mut m = IndexMap::new(); m.insert( "config".to_string(), Box::new(PhpMixed::Array(IndexMap::new())), ); m }))?; let _ = Silencer::call(|| { chmod(&path, 0o600); Ok(()) }); } if !self.config_file().as_ref().unwrap().exists() { return Err(anyhow::anyhow!( "File \"{}\" cannot be found in the current directory", config_file )); } Ok(()) } /// Get the local composer.json, global config.json, or the file passed by the user fn get_composer_config_file(&self, input: &dyn InputInterface, config: &Config) -> String { if input.get_option("global").as_bool().unwrap_or(false) { format!("{}/config.json", config.get("home")) } else { input .get_option("file") .as_string() .map(|s| s.to_string()) .unwrap_or_else(|| Factory::get_composer_file().unwrap_or_default()) } } /// Get the local auth.json or global auth.json, or if the user passed in a file to use, /// the corresponding auth.json fn get_auth_config_file(&self, input: &dyn InputInterface, config: &Config) -> String { if input.get_option("global").as_bool().unwrap_or(false) { format!("{}/auth.json", config.get("home")) } else { let composer_config = self.get_composer_config_file(input, config); let parent = std::path::Path::new(&composer_config) .parent() .map(|p| p.to_string_lossy().to_string()) .unwrap_or_default(); format!("{}/auth.json", parent) } } }