diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-07 10:33:53 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-07 10:33:53 +0900 |
| commit | 971824aa15334fd12d08ae0f441f6bf6079344c3 (patch) | |
| tree | d59892042f478c65d980d96f62ef633644fd8fea /crates/shirabe/src/command | |
| parent | 0d45c403c20ac8950f2877a21f9cd4f0ca9bf784 (diff) | |
| download | php-shirabe-971824aa15334fd12d08ae0f441f6bf6079344c3.tar.gz php-shirabe-971824aa15334fd12d08ae0f441f6bf6079344c3.tar.zst php-shirabe-971824aa15334fd12d08ae0f441f6bf6079344c3.zip | |
feat(shirabe): resolve phase-b TODOs with shared ownership
Replace TODO(phase-b) placeholders (todo!() and commented-out code)
with real implementations:
- Share JsonFile via Rc<RefCell<JsonFile>> so JsonConfigSource and the
owning command can hold the same instance (base_config_command,
config_command, repository_command, require_command, create_project,
remove_command, factory)
- Change InstallerInterface methods (is_installed, download, prepare,
cleanup, get_install_path) to &mut self so initialize_vendor_dir can
run, propagated to all installer implementations
- Pass io/config/filesystem/process by clone instead of moving or
stubbing (auth_helper, svn_driver, curl_downloader, library_installer)
- Make TransportException Clone and store it by value in VcsRepository
- Clone operations in Transaction sort, root_aliases/temporary_constraints
in RepositorySet::create_pool, and share CompletePackage via handle in
PlatformRepository
- Wire up set_option, set_requires/set_dev_requires, installation manager
setters, BumpCommand::set_composer, and clean_backups/set_local_phar
Diffstat (limited to 'crates/shirabe/src/command')
| -rw-r--r-- | crates/shirabe/src/command/base_command.rs | 9 | ||||
| -rw-r--r-- | crates/shirabe/src/command/base_config_command.rs | 25 | ||||
| -rw-r--r-- | crates/shirabe/src/command/config_command.rs | 80 | ||||
| -rw-r--r-- | crates/shirabe/src/command/create_project_command.rs | 15 | ||||
| -rw-r--r-- | crates/shirabe/src/command/package_discovery_trait.rs | 12 | ||||
| -rw-r--r-- | crates/shirabe/src/command/reinstall_command.rs | 20 | ||||
| -rw-r--r-- | crates/shirabe/src/command/remove_command.rs | 12 | ||||
| -rw-r--r-- | crates/shirabe/src/command/repository_command.rs | 20 | ||||
| -rw-r--r-- | crates/shirabe/src/command/require_command.rs | 75 | ||||
| -rw-r--r-- | crates/shirabe/src/command/self_update_command.rs | 7 | ||||
| -rw-r--r-- | crates/shirabe/src/command/show_command.rs | 34 | ||||
| -rw-r--r-- | crates/shirabe/src/command/update_command.rs | 4 |
12 files changed, 163 insertions, 150 deletions
diff --git a/crates/shirabe/src/command/base_command.rs b/crates/shirabe/src/command/base_command.rs index 8aff0d9..24b5a2b 100644 --- a/crates/shirabe/src/command/base_command.rs +++ b/crates/shirabe/src/command/base_command.rs @@ -564,11 +564,14 @@ impl<C: HasBaseCommandData> BaseCommand for C { let prefer_install = input.borrow().get_option("prefer-install"); match prefer_install.as_string().unwrap_or("") { "dist" => { - // TODO(phase-b): InputInterface set_option needs &mut self - let _ = "input.set_option('prefer-dist', true)"; + input + .borrow_mut() + .set_option("prefer-dist", PhpMixed::Bool(true))?; } "source" => { - let _ = "input.set_option('prefer-source', true)"; + input + .borrow_mut() + .set_option("prefer-source", PhpMixed::Bool(true))?; } "auto" => { prefer_dist = false; diff --git a/crates/shirabe/src/command/base_config_command.rs b/crates/shirabe/src/command/base_config_command.rs index 3aed839..d681870 100644 --- a/crates/shirabe/src/command/base_config_command.rs +++ b/crates/shirabe/src/command/base_config_command.rs @@ -15,9 +15,8 @@ use shirabe_php_shim::{PhpMixed, chmod, touch}; pub trait BaseConfigCommand: BaseCommand { fn config(&self) -> Option<&std::rc::Rc<std::cell::RefCell<Config>>>; fn config_mut(&mut self) -> &mut Option<std::rc::Rc<std::cell::RefCell<Config>>>; - fn config_file(&self) -> Option<&JsonFile>; - fn config_file_mut(&mut self) -> Option<&mut JsonFile>; - fn set_config_file(&mut self, file: Option<JsonFile>); + fn config_file(&self) -> Option<&std::rc::Rc<std::cell::RefCell<JsonFile>>>; + fn set_config_file(&mut self, file: Option<std::rc::Rc<std::cell::RefCell<JsonFile>>>); fn config_source(&self) -> Option<&JsonConfigSource>; fn config_source_mut(&mut self) -> Option<&mut JsonConfigSource>; fn set_config_source(&mut self, source: Option<JsonConfigSource>); @@ -40,8 +39,7 @@ pub trait BaseConfigCommand: BaseCommand { 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(); + let io = self.get_io(); *self.config_mut() = Some(std::rc::Rc::new(std::cell::RefCell::new( Factory::create_config(Some(io.clone()), None)?, ))); @@ -68,14 +66,13 @@ pub trait BaseConfigCommand: BaseCommand { { std::fs::write(&config_file, "{\n}\n")?; } - self.set_config_file(Some(JsonFile::new( + let config_file_jf = std::rc::Rc::new(std::cell::RefCell::new(JsonFile::new( config_file.clone(), None, Some(io.clone()), )?)); - // TODO(phase-b): JsonConfigSource::new takes owned JsonFile, but PHP shares the same - // instance with $this->configFile. Needs Rc<RefCell<JsonFile>> refactor on both sides. - self.set_config_source(None); + self.set_config_file(Some(config_file_jf.clone())); + self.set_config_source(Some(JsonConfigSource::new(config_file_jf, false))); // Initialize the global file if it's not there, ignoring any warnings or notices if input @@ -83,13 +80,13 @@ pub trait BaseConfigCommand: BaseCommand { .get_option("global") .as_bool() .unwrap_or(false) - && !self.config_file().as_ref().unwrap().exists() + && !self.config_file().unwrap().borrow().exists() { - let path = self.config_file().as_ref().unwrap().get_path().to_string(); + let path = self.config_file().unwrap().borrow().get_path().to_string(); touch(&path); - self.config_file_mut() - .as_mut() + self.config_file() .unwrap() + .borrow() .write(PhpMixed::Array({ let mut m = IndexMap::new(); m.insert( @@ -104,7 +101,7 @@ pub trait BaseConfigCommand: BaseCommand { }); } - if !self.config_file().as_ref().unwrap().exists() { + if !self.config_file().unwrap().borrow().exists() { return Err(anyhow::anyhow!( "File \"{}\" cannot be found in the current directory", config_file diff --git a/crates/shirabe/src/command/config_command.rs b/crates/shirabe/src/command/config_command.rs index 38b8a8d..945dfcb 100644 --- a/crates/shirabe/src/command/config_command.rs +++ b/crates/shirabe/src/command/config_command.rs @@ -38,10 +38,10 @@ pub struct ConfigCommand { base_command_data: BaseCommandData, config: Option<std::rc::Rc<std::cell::RefCell<Config>>>, - config_file: Option<JsonFile>, + config_file: Option<std::rc::Rc<std::cell::RefCell<JsonFile>>>, config_source: Option<JsonConfigSource>, - pub(crate) auth_config_file: Option<JsonFile>, + pub(crate) auth_config_file: Option<std::rc::Rc<std::cell::RefCell<JsonFile>>>, pub(crate) auth_config_source: Option<JsonConfigSource>, } @@ -128,20 +128,19 @@ impl ConfigCommand { let auth_config_file = self.get_auth_config_file(input.clone(), &*self.config.as_ref().unwrap().borrow()); - self.auth_config_file = Some(JsonFile::new( + let auth_config_file_jf = std::rc::Rc::new(std::cell::RefCell::new(JsonFile::new( auth_config_file, None, Some(self.get_io().clone()), - )?); - // 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; + )?)); + self.auth_config_file = Some(auth_config_file_jf.clone()); + self.auth_config_source = Some(JsonConfigSource::new(auth_config_file_jf, true)); // Initialize the global file if it's not there, ignoring any warnings or notices if input.borrow().get_option("global").as_bool() == Some(true) - && !self.auth_config_file.as_ref().unwrap().exists() + && !self.auth_config_file.as_ref().unwrap().borrow().exists() { - touch(self.auth_config_file.as_ref().unwrap().get_path()); + touch(self.auth_config_file.as_ref().unwrap().borrow().get_path()); let mut empty_objs: IndexMap<String, Box<PhpMixed>> = IndexMap::new(); for k in &[ "bitbucket-oauth", @@ -158,13 +157,15 @@ impl ConfigCommand { ); } self.auth_config_file - .as_mut() + .as_ref() .unwrap() + .borrow() .write(PhpMixed::Array(empty_objs))?; let path_clone = self .auth_config_file .as_ref() .unwrap() + .borrow() .get_path() .to_string(); Silencer::call(|| { @@ -205,10 +206,16 @@ impl ConfigCommand { self.auth_config_file .as_ref() .unwrap() + .borrow() .get_path() .to_string() } else { - self.config_file.as_ref().unwrap().get_path().to_string() + self.config_file + .as_ref() + .unwrap() + .borrow() + .get_path() + .to_string() }; system( &format!( @@ -228,7 +235,7 @@ impl ConfigCommand { } if input.borrow().get_option("global").as_bool() != Some(true) { - let config_read = self.config_file.as_mut().unwrap().read()?; + let config_read = self.config_file.as_ref().unwrap().borrow_mut().read()?; let config_map = match config_read { PhpMixed::Array(m) => m .into_iter() @@ -236,23 +243,25 @@ impl ConfigCommand { .collect::<IndexMap<String, PhpMixed>>(), _ => IndexMap::new(), }; - self.config - .as_mut() - .unwrap() - .borrow_mut() - .merge(&config_map, self.config_file.as_ref().unwrap().get_path()); - let auth_data: PhpMixed = if self.auth_config_file.as_ref().unwrap().exists() { - self.auth_config_file.as_mut().unwrap().read()? + self.config.as_mut().unwrap().borrow_mut().merge( + &config_map, + self.config_file.as_ref().unwrap().borrow().get_path(), + ); + let auth_data: PhpMixed = if self.auth_config_file.as_ref().unwrap().borrow().exists() { + self.auth_config_file + .as_ref() + .unwrap() + .borrow_mut() + .read()? } else { PhpMixed::Array(IndexMap::new()) }; let mut wrap: IndexMap<String, PhpMixed> = IndexMap::new(); wrap.insert("config".to_string(), auth_data); - self.config - .as_mut() - .unwrap() - .borrow_mut() - .merge(&wrap, self.auth_config_file.as_ref().unwrap().get_path()); + self.config.as_mut().unwrap().borrow_mut().merge( + &wrap, + self.auth_config_file.as_ref().unwrap().borrow().get_path(), + ); } { @@ -321,7 +330,7 @@ impl ConfigCommand { properties_defaults.insert("license".to_string(), PhpMixed::List(vec![])); 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_mut().unwrap().read()?; + let raw_data = self.config_file.as_ref().unwrap().borrow_mut().read()?; let mut data = self.config.as_ref().unwrap().borrow_mut().all(0)?; let mut source = self .config @@ -464,7 +473,13 @@ impl ConfigCommand { && in_array(setting_key.as_str().into(), &properties.into(), true) { value = (**raw_data.as_array().unwrap().get(&setting_key).unwrap()).clone(); - source = self.config_file.as_ref().unwrap().get_path().to_string(); + source = self + .config_file + .as_ref() + .unwrap() + .borrow() + .get_path() + .to_string(); } else if let Some(v) = properties_defaults.get(&setting_key) { value = v.clone(); source = "defaults".to_string(); @@ -772,7 +787,8 @@ impl ConfigCommand { if input.borrow().get_option("json").as_bool() == Some(true) { value = JsonFile::parse_json(Some(&values[0]), Some("composer.json"))?; if input.borrow().get_option("merge").as_bool() == Some(true) { - let current_value_outer = self.config_file.as_mut().unwrap().read()?; + let current_value_outer = + self.config_file.as_ref().unwrap().borrow_mut().read()?; let bits = explode(".", &setting_key); let mut current_value: PhpMixed = current_value_outer; for bit in &bits { @@ -922,7 +938,7 @@ impl ConfigCommand { } if input.borrow().get_option("merge").as_bool() == Some(true) { - let current_config = self.config_file.as_mut().unwrap().read()?; + let current_config = self.config_file.as_ref().unwrap().borrow_mut().read()?; let key_suffix = str_replace("audit.", "", &setting_key); let current_value = current_config .as_array() @@ -2071,15 +2087,11 @@ impl BaseConfigCommand for ConfigCommand { &mut self.config } - fn config_file(&self) -> Option<&JsonFile> { + fn config_file(&self) -> Option<&std::rc::Rc<std::cell::RefCell<JsonFile>>> { self.config_file.as_ref() } - fn config_file_mut(&mut self) -> Option<&mut JsonFile> { - self.config_file.as_mut() - } - - fn set_config_file(&mut self, file: Option<JsonFile>) { + fn set_config_file(&mut self, file: Option<std::rc::Rc<std::cell::RefCell<JsonFile>>>) { self.config_file = file; } diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs index d05f27d..0d5708d 100644 --- a/crates/shirabe/src/command/create_project_command.rs +++ b/crates/shirabe/src/command/create_project_command.rs @@ -113,8 +113,7 @@ impl CreateProjectCommand { _output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>, ) -> Result<i64> { let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?)); - // TODO(phase-b): get_io returns &mut Self-borrow; clone_box for an owned Box to dodge. - let io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = self.get_io().clone(); + let io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = self.get_io(); let (prefer_source, prefer_dist) = self.get_preferred_install_options(&config.borrow(), input.clone(), true)?; @@ -345,7 +344,11 @@ impl CreateProjectCommand { &placeholder_existing, ); let mut config_source = JsonConfigSource::new( - JsonFile::new("composer.json".to_string(), None, None)?, + std::rc::Rc::new(std::cell::RefCell::new(JsonFile::new( + "composer.json".to_string(), + None, + None, + )?)), false, ); @@ -544,7 +547,11 @@ impl CreateProjectCommand { if !has_vcs { let package = composer.get_package(); let mut config_source = JsonConfigSource::new( - JsonFile::new("composer.json".to_string(), None, None)?, + std::rc::Rc::new(std::cell::RefCell::new(JsonFile::new( + "composer.json".to_string(), + None, + None, + )?)), false, ); for (r#type, meta) in SUPPORTED_LINK_TYPES.iter() { diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs index 9bd49c2..4162676 100644 --- a/crates/shirabe/src/command/package_discovery_trait.rs +++ b/crates/shirabe/src/command/package_discovery_trait.rs @@ -260,8 +260,11 @@ pub trait PackageDiscoveryTrait { Some(s) => s.to_string(), None => break, }; - // TODO(phase-b): self.get_repos() (&mut self) conflicts with io borrow (&self) - let mut matches: Vec<SearchResult> = todo!("self.get_repos().search()"); + let mut matches: Vec<SearchResult> = self.get_repos().search( + package.clone(), + crate::repository::repository_interface::SEARCH_FULLTEXT, + None, + )?; if count(&PhpMixed::List( matches.iter().map(|_| Box::new(PhpMixed::Null)).collect(), @@ -291,9 +294,8 @@ pub trait PackageDiscoveryTrait { // no match, prompt which to pick if !exact_match { - // TODO(phase-b): self.get_repos() (&mut self) conflicts with io borrow (&self) let providers: IndexMap<String, crate::repository::ProviderInfo> = - todo!("self.get_repos().get_providers()"); + self.get_repos().get_providers(package.clone())?; if count(&PhpMixed::List( providers.iter().map(|_| Box::new(PhpMixed::Null)).collect(), )) > 0 @@ -449,7 +451,7 @@ pub trait PackageDiscoveryTrait { let (_name, c): (String, String) = self .find_best_version_and_name_for_package( io.clone(), - input, + input.clone(), &package, platform_repo, preferred_stability, diff --git a/crates/shirabe/src/command/reinstall_command.rs b/crates/shirabe/src/command/reinstall_command.rs index 6e4cd7b..e72a177 100644 --- a/crates/shirabe/src/command/reinstall_command.rs +++ b/crates/shirabe/src/command/reinstall_command.rs @@ -195,17 +195,21 @@ impl ReinstallCommand { let download_manager = composer.get_download_manager(); let package = composer.get_package(); - // TODO(phase-b): InstallationManager setters need &mut self; conflicts with the &installation_manager / &local_repo / &package borrows held below; needs shared-ownership refactor - let _no_progress = !input - .borrow() - .get_option("no-progress") - .as_bool() - .unwrap_or(false); - let _no_plugins = input + installation_manager.borrow_mut().set_output_progress( + !input + .borrow() + .get_option("no-progress") + .as_bool() + .unwrap_or(false), + ); + if input .borrow() .get_option("no-plugins") .as_bool() - .unwrap_or(false); + .unwrap_or(false) + { + installation_manager.borrow_mut().disable_plugins(); + } download_manager .borrow_mut() diff --git a/crates/shirabe/src/command/remove_command.rs b/crates/shirabe/src/command/remove_command.rs index 2cf71e7..c2a358b 100644 --- a/crates/shirabe/src/command/remove_command.rs +++ b/crates/shirabe/src/command/remove_command.rs @@ -270,7 +270,10 @@ impl RemoveCommand { let composer_backup = std::fs::read_to_string(json_file.get_path())?; let json_file_for_source = JsonFile::new(file.clone(), None, None)?; - let mut json = JsonConfigSource::new(json_file_for_source, false); + let mut json = JsonConfigSource::new( + std::rc::Rc::new(std::cell::RefCell::new(json_file_for_source)), + false, + ); let r#type = if input.borrow().get_option("dev").as_bool().unwrap_or(false) { "require-dev" @@ -464,7 +467,6 @@ impl RemoveCommand { let mut composer = crate::command::composer_full_mut(&composer_handle); if dry_run { - // TODO(phase-b): composer.get_package() returns &dyn RootPackageInterface; set_requires/set_dev_requires need &mut self; needs shared-ownership refactor let root_package = composer.get_package(); let mut links: IndexMap<String, IndexMap<String, _>> = IndexMap::new(); links.insert("require".to_string(), root_package.get_requires().clone()); @@ -479,10 +481,8 @@ impl RemoveCommand { } } } - let _ = links.remove("require").unwrap_or_default(); - let _ = links.remove("require-dev").unwrap_or_default(); - // root_package.set_requires(links.remove("require").unwrap_or_default().into_values().collect()); - // root_package.set_dev_requires(links.remove("require-dev").unwrap_or_default().into_values().collect()); + root_package.set_requires(links.remove("require").unwrap_or_default()); + root_package.set_dev_requires(links.remove("require-dev").unwrap_or_default()); } // TODO(plugin): dispatch CommandEvent(PluginEvents::COMMAND, 'remove', input, output) diff --git a/crates/shirabe/src/command/repository_command.rs b/crates/shirabe/src/command/repository_command.rs index 5ced013..975dd32 100644 --- a/crates/shirabe/src/command/repository_command.rs +++ b/crates/shirabe/src/command/repository_command.rs @@ -25,7 +25,7 @@ pub struct RepositoryCommand { base_command_data: BaseCommandData, config: Option<std::rc::Rc<std::cell::RefCell<Config>>>, - config_file: Option<JsonFile>, + config_file: Option<std::rc::Rc<std::cell::RefCell<JsonFile>>>, config_source: Option<JsonConfigSource>, } @@ -95,8 +95,14 @@ impl RepositoryCommand { .as_string() .map(|s| s.to_string()); - let config_data = self.config_file.as_mut().unwrap().read()?; - let config_file_path = self.config_file.as_ref().unwrap().get_path().to_string(); + let config_data = self.config_file.as_ref().unwrap().borrow_mut().read()?; + let config_file_path = self + .config_file + .as_ref() + .unwrap() + .borrow() + .get_path() + .to_string(); let config_data_map: IndexMap<String, PhpMixed> = match config_data { PhpMixed::Array(m) => m.into_iter().map(|(k, v)| (k, *v)).collect(), _ => IndexMap::new(), @@ -416,14 +422,10 @@ impl BaseConfigCommand for RepositoryCommand { &mut self.config } - fn config_file(&self) -> Option<&JsonFile> { + fn config_file(&self) -> Option<&std::rc::Rc<std::cell::RefCell<JsonFile>>> { self.config_file.as_ref() } - fn config_file_mut(&mut self) -> Option<&mut JsonFile> { - self.config_file.as_mut() - } - fn config_source(&self) -> Option<&JsonConfigSource> { self.config_source.as_ref() } @@ -432,7 +434,7 @@ impl BaseConfigCommand for RepositoryCommand { self.config_source.as_mut() } - fn set_config_file(&mut self, file: Option<JsonFile>) { + fn set_config_file(&mut self, file: Option<std::rc::Rc<std::cell::RefCell<JsonFile>>>) { self.config_file = file; } diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs index d1143fe..871b07a 100644 --- a/crates/shirabe/src/command/require_command.rs +++ b/crates/shirabe/src/command/require_command.rs @@ -50,7 +50,7 @@ pub struct RequireCommand { newly_created: bool, first_require: bool, - json: Option<JsonFile>, + json: Option<std::rc::Rc<std::cell::RefCell<JsonFile>>>, file: String, composer_backup: String, /// file name @@ -186,10 +186,14 @@ impl RequireCommand { file_put_contents(&self.file, b"{\n}\n"); } - self.json = Some(JsonFile::new(self.file.clone(), None, None)?); + self.json = Some(std::rc::Rc::new(std::cell::RefCell::new(JsonFile::new( + self.file.clone(), + None, + None, + )?))); self.lock = Factory::get_lock_file(&self.file); self.composer_backup = - file_get_contents(self.json.as_ref().unwrap().get_path()).unwrap_or_default(); + file_get_contents(self.json.as_ref().unwrap().borrow().get_path()).unwrap_or_default(); self.lock_backup = if file_exists(&self.lock) { file_get_contents(&self.lock) } else { @@ -231,7 +235,7 @@ impl RequireCommand { } if input.borrow().get_option("fixed").as_bool() == Some(true) { - let config = self.json.as_mut().unwrap().read()?; + let config = self.json.as_ref().unwrap().borrow_mut().read()?; let package_type = if empty(&config.get("type").cloned().unwrap_or(PhpMixed::Null)) { "library".to_string() @@ -527,7 +531,7 @@ impl RequireCommand { self.first_require = self.newly_created; if !self.first_require { - let composer_definition = self.json.as_mut().unwrap().read()?; + let composer_definition = self.json.as_ref().unwrap().borrow_mut().read()?; let require_count = composer_definition .get("require") .and_then(|v| v.as_array()) @@ -549,18 +553,8 @@ impl RequireCommand { .as_bool() .unwrap_or(false) { - // TODO(phase-b): update_file takes &mut self, but the json argument must be borrowed - // from self.json, producing an overlapping borrow of self. Commented out until JsonFile - // is shared (Rc<RefCell> / interior mutability) so it can be passed without holding a - // borrow of self. - // self.update_file( - // self.json.as_ref().unwrap(), - // &requirements, - // require_key, - // remove_key, - // sort_packages, - // ); - let _ = sort_packages; + let json = self.json.as_ref().unwrap().clone(); + self.update_file(&json, &requirements, require_key, remove_key, sort_packages); } let updated_msg = format!( @@ -635,7 +629,7 @@ impl RequireCommand { // finally if dry_run && self.newly_created { // @unlink($this->json->getPath()); - unlink(self.json.as_ref().unwrap().get_path()); + unlink(self.json.as_ref().unwrap().borrow().get_path()); } signal_handler.unregister(); @@ -665,7 +659,13 @@ impl RequireCommand { /// @return array<string, string> fn get_packages_by_require_key(&mut self) -> IndexMap<String, String> { - let composer_definition = self.json.as_mut().unwrap().read().unwrap_or_default(); + let composer_definition = self + .json + .as_ref() + .unwrap() + .borrow_mut() + .read() + .unwrap_or_default(); let mut require: IndexMap<String, PhpMixed> = IndexMap::new(); let mut require_dev: IndexMap<String, PhpMixed> = IndexMap::new(); @@ -1122,18 +1122,8 @@ impl RequireCommand { } if !dry_run { - // TODO(phase-b): update_file takes &mut self while self.json is borrowed; needs - // refactor to pass the JsonFile owned/cloned or use interior mutability. - let json_path = self.json.as_ref().unwrap().get_path().to_string(); - let _ = ( - json_path, - &requirements, - require_key, - remove_key, - sort_packages, - ); - todo!("call self.update_file without overlapping borrows of self.json"); - #[allow(unreachable_code)] + let json = self.json.as_ref().unwrap().clone(); + self.update_file(&json, &requirements, require_key, remove_key, sort_packages); if locker_is_locked && composer .get_config() @@ -1160,7 +1150,7 @@ impl RequireCommand { /// @param array<string, string> $new fn update_file( &mut self, - json: &JsonFile, + json: &std::rc::Rc<std::cell::RefCell<JsonFile>>, new: &IndexMap<String, String>, require_key: &str, remove_key: &str, @@ -1170,7 +1160,7 @@ impl RequireCommand { return; } - let composer_definition_mixed = self.json.as_mut().unwrap().read().unwrap_or_default(); + let composer_definition_mixed = json.borrow_mut().read().unwrap_or_default(); let mut composer_definition: IndexMap<String, Box<PhpMixed>> = composer_definition_mixed .as_array() .cloned() @@ -1197,23 +1187,19 @@ impl RequireCommand { composer_definition.shift_remove(remove_key); } } - let _ = self - .json - .as_ref() - .unwrap() - .write(PhpMixed::Array(composer_definition)); + let _ = json.borrow().write(PhpMixed::Array(composer_definition)); } /// @param array<string, string> $new fn update_file_cleanly( &self, - json: &JsonFile, + json: &std::rc::Rc<std::cell::RefCell<JsonFile>>, new: &IndexMap<String, String>, require_key: &str, remove_key: &str, sort_packages: bool, ) -> bool { - let contents = file_get_contents(json.get_path()).unwrap_or_default(); + let contents = file_get_contents(json.borrow().get_path()).unwrap_or_default(); let mut manipulator = match JsonManipulator::new(contents) { Ok(m) => m, @@ -1237,7 +1223,10 @@ impl RequireCommand { let _ = manipulator.remove_main_key_if_empty(remove_key); - file_put_contents(json.get_path(), manipulator.get_contents().as_bytes()); + file_put_contents( + json.borrow().get_path(), + manipulator.get_contents().as_bytes(), + ); true } @@ -1256,7 +1245,7 @@ impl RequireCommand { self.file ); self.get_io().write_error3(&msg, true, io_interface::NORMAL); - unlink(self.json.as_ref().unwrap().get_path()); + unlink(self.json.as_ref().unwrap().borrow().get_path()); if file_exists(&self.lock) { unlink(&self.lock); } @@ -1272,7 +1261,7 @@ impl RequireCommand { ); self.get_io().write_error3(&msg, true, io_interface::NORMAL); file_put_contents( - self.json.as_ref().unwrap().get_path(), + self.json.as_ref().unwrap().borrow().get_path(), self.composer_backup.as_bytes(), ); if let Some(ref lock_backup) = self.lock_backup { diff --git a/crates/shirabe/src/command/self_update_command.rs b/crates/shirabe/src/command/self_update_command.rs index 2353d13..9c5ae5a 100644 --- a/crates/shirabe/src/command/self_update_command.rs +++ b/crates/shirabe/src/command/self_update_command.rs @@ -646,19 +646,16 @@ RGv89BPD+2DLnJysngsvVaUCAwEAAQ==\n\ .as_bool() .unwrap_or(false) { - // TODO(phase-b): self.clean_backups conflicts with earlier `self.get_io()` borrow + self.clean_backups(&rollback_dir, None); } - // TODO(phase-b): self.set_local_phar mutable borrow conflicts with earlier `self.get_io()` borrow - let _set_phar_ok: bool = todo!("self.set_local_phar(...)"); - if !_set_phar_ok { + if !self.set_local_phar(&local_filename, &temp_filename, Some(&backup_file))? { // @unlink let _ = unlink(&temp_filename); return Ok(1); } - // TODO(phase-b): re-borrow io because earlier borrow conflicts with the &mut self calls above let io = self.get_io(); if file_exists(&backup_file) { io.write_error3( diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs index 859ba97..4f771a8 100644 --- a/crates/shirabe/src/command/show_command.rs +++ b/crates/shirabe/src/command/show_command.rs @@ -373,11 +373,7 @@ impl ShowCommand { locked_repo = Some(lr_handle); } else { // --installed / default case - // TODO(phase-b): PHP shares the Composer object by reference. Phase B - // can't clone Composer, so we re-fetch via require_composer when missing - // but otherwise borrow the existing Option. let composer_local_owned; - // Borrow guards that keep the Ref alive for the duration of the block. let _guard_from_existing; let composer_local = match composer.as_ref() { Some(c) => { @@ -1084,11 +1080,14 @@ impl ShowCommand { } } if write_path { - // TODO(phase-b): get_installation_manager wants &mut Composer; PHP shares by ref. - let path: Option<String> = { - let _ = composer.as_ref().unwrap(); - None - }; + let installation_manager = composer + .as_ref() + .unwrap() + .borrow_partial() + .get_installation_manager(); + let path: Option<String> = installation_manager + .borrow_mut() + .get_install_path(package.clone()); if let Some(p) = path { let r = realpath(&p).unwrap_or_default(); let trimmed = @@ -1702,11 +1701,12 @@ impl ShowCommand { package.get_dist_reference().unwrap_or_default() )); if is_installed_package { - // TODO(phase-b): get_installation_manager wants &mut Composer; PHP shares by ref. - // Skipping the install path lookup keeps compile clean. let path: Option<String> = self.require_composer(None, None).ok().and_then(|c| { - let _ = c; - None::<String> + let installation_manager = c.borrow_partial().get_installation_manager(); + let p = installation_manager + .borrow_mut() + .get_install_path(package.clone().into()); + p }); if let Some(p) = path { self.get_io().write(&format!( @@ -1982,9 +1982,11 @@ impl ShowCommand { if !PlatformRepository::is_platform_package(&package.get_name()) && installed_repo.has_package(package.clone().into()) { - // TODO(phase-b): get_installation_manager wants &mut Composer; PHP shares by ref. - let _ = self.require_composer(None, None)?; - let path: Option<String> = None; + let composer = self.require_composer(None, None)?; + let installation_manager = composer.borrow_partial().get_installation_manager(); + let path: Option<String> = installation_manager + .borrow_mut() + .get_install_path(package.clone().into()); match path { Some(p) => { if let Some(r) = realpath(&p) { diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs index 7731e4f..4064e57 100644 --- a/crates/shirabe/src/command/update_command.rs +++ b/crates/shirabe/src/command/update_command.rs @@ -518,9 +518,7 @@ impl UpdateCommand { io_interface::NORMAL, ); let mut bump_command = BumpCommand::new(None); - // TODO(phase-b): Composer is a PHP class shared by reference; calling - // set_composer here requires a shared PartialComposer handle. - // bump_command.set_composer(composer); + bump_command.set_composer(composer_handle.clone()); result = bump_command.do_bump( io.clone(), bump_after_update.as_string() == Some("dev"), |
