aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/command
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-07 10:33:53 +0900
committernsfisis <nsfisis@gmail.com>2026-06-07 10:33:53 +0900
commit971824aa15334fd12d08ae0f441f6bf6079344c3 (patch)
treed59892042f478c65d980d96f62ef633644fd8fea /crates/shirabe/src/command
parent0d45c403c20ac8950f2877a21f9cd4f0ca9bf784 (diff)
downloadphp-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.rs9
-rw-r--r--crates/shirabe/src/command/base_config_command.rs25
-rw-r--r--crates/shirabe/src/command/config_command.rs80
-rw-r--r--crates/shirabe/src/command/create_project_command.rs15
-rw-r--r--crates/shirabe/src/command/package_discovery_trait.rs12
-rw-r--r--crates/shirabe/src/command/reinstall_command.rs20
-rw-r--r--crates/shirabe/src/command/remove_command.rs12
-rw-r--r--crates/shirabe/src/command/repository_command.rs20
-rw-r--r--crates/shirabe/src/command/require_command.rs75
-rw-r--r--crates/shirabe/src/command/self_update_command.rs7
-rw-r--r--crates/shirabe/src/command/show_command.rs34
-rw-r--r--crates/shirabe/src/command/update_command.rs4
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"),