aboutsummaryrefslogtreecommitdiffhomepage
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
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
-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
-rw-r--r--crates/shirabe/src/config/json_config_source.rs39
-rw-r--r--crates/shirabe/src/dependency_resolver/transaction.rs24
-rw-r--r--crates/shirabe/src/downloader/transport_exception.rs2
-rw-r--r--crates/shirabe/src/factory.rs16
-rw-r--r--crates/shirabe/src/installer.rs5
-rw-r--r--crates/shirabe/src/installer/installer_interface.rs10
-rw-r--r--crates/shirabe/src/installer/library_installer.rs42
-rw-r--r--crates/shirabe/src/installer/metapackage_installer.rs10
-rw-r--r--crates/shirabe/src/installer/noop_installer.rs10
-rw-r--r--crates/shirabe/src/installer/plugin_installer.rs10
-rw-r--r--crates/shirabe/src/installer/project_installer.rs10
-rw-r--r--crates/shirabe/src/plugin/plugin_manager.rs12
-rw-r--r--crates/shirabe/src/repository/platform_repository.rs15
-rw-r--r--crates/shirabe/src/repository/repository_set.rs33
-rw-r--r--crates/shirabe/src/repository/vcs/svn_driver.rs10
-rw-r--r--crates/shirabe/src/repository/vcs_repository.rs19
-rw-r--r--crates/shirabe/src/util/auth_helper.rs38
-rw-r--r--crates/shirabe/src/util/forgejo.rs2
-rw-r--r--crates/shirabe/src/util/github.rs2
-rw-r--r--crates/shirabe/src/util/http/curl_downloader.rs4
32 files changed, 299 insertions, 327 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"),
diff --git a/crates/shirabe/src/config/json_config_source.rs b/crates/shirabe/src/config/json_config_source.rs
index 5d81793..532c1d8 100644
--- a/crates/shirabe/src/config/json_config_source.rs
+++ b/crates/shirabe/src/config/json_config_source.rs
@@ -18,7 +18,7 @@ use crate::util::Filesystem;
#[derive(Debug)]
pub struct JsonConfigSource {
/// @var JsonFile
- file: JsonFile,
+ file: std::rc::Rc<std::cell::RefCell<JsonFile>>,
/// @var bool
auth_config: bool,
@@ -26,7 +26,7 @@ pub struct JsonConfigSource {
impl JsonConfigSource {
/// Constructor
- pub fn new(file: JsonFile, auth_config: bool) -> Self {
+ pub fn new(file: std::rc::Rc<std::cell::RefCell<JsonFile>>, auth_config: bool) -> Self {
Self { file, auth_config }
}
@@ -39,30 +39,30 @@ impl JsonConfigSource {
mut args: Vec<PhpMixed>,
) -> Result<()> {
let contents;
- if self.file.exists() {
- if !is_writable(self.file.get_path()) {
+ if self.file.borrow().exists() {
+ if !is_writable(self.file.borrow().get_path()) {
return Err(RuntimeException {
message: sprintf(
"The file \"%s\" is not writable.",
- &[PhpMixed::String(self.file.get_path().to_string())],
+ &[PhpMixed::String(self.file.borrow().get_path().to_string())],
),
code: 0,
}
.into());
}
- if !Filesystem::is_readable(self.file.get_path()) {
+ if !Filesystem::is_readable(self.file.borrow().get_path()) {
return Err(RuntimeException {
message: sprintf(
"The file \"%s\" is not readable.",
- &[PhpMixed::String(self.file.get_path().to_string())],
+ &[PhpMixed::String(self.file.borrow().get_path().to_string())],
),
code: 0,
}
.into());
}
- contents = file_get_contents(self.file.get_path()).unwrap_or_default();
+ contents = file_get_contents(self.file.borrow().get_path()).unwrap_or_default();
} else if self.auth_config {
contents = "{\n}\n".to_string();
} else {
@@ -71,7 +71,7 @@ impl JsonConfigSource {
let mut manipulator = JsonManipulator::new(contents.clone())?;
- let new_file = !self.file.exists();
+ let new_file = !self.file.borrow().exists();
// override manipulator method for auth config files
let mut method = method.to_string();
@@ -103,10 +103,13 @@ impl JsonConfigSource {
.as_bool()
.unwrap_or(false);
if manipulator_result {
- file_put_contents(self.file.get_path(), manipulator.get_contents().as_bytes());
+ file_put_contents(
+ self.file.borrow().get_path(),
+ manipulator.get_contents().as_bytes(),
+ );
} else {
// on failed clean update, call the fallback and rewrite the whole file
- let mut config = self.file.read()?;
+ let mut config = self.file.borrow_mut().read()?;
self.array_unshift_ref(&mut args, &mut config);
fallback(&mut config, &mut args);
// avoid ending up with arrays for keys that should be objects
@@ -199,17 +202,21 @@ impl JsonConfigSource {
}
}
}
- self.file.write(config)?;
+ self.file.borrow().write(config)?;
}
- match self.file.validate_schema(JsonFile::LAX_SCHEMA, None) {
+ match self
+ .file
+ .borrow()
+ .validate_schema(JsonFile::LAX_SCHEMA, None)
+ {
Ok(_) => {}
Err(e) => {
let Some(jve) = e.downcast_ref::<JsonValidationException>() else {
return Err(e);
};
// restore contents to the original state
- file_put_contents(self.file.get_path(), contents.as_bytes());
+ file_put_contents(self.file.borrow().get_path(), contents.as_bytes());
return Err(RuntimeException {
message: format!(
"Failed to update composer.json with a valid format, reverting to the original content. Please report an issue to us with details (command you run and a copy of your composer.json). {}{}",
@@ -223,7 +230,7 @@ impl JsonConfigSource {
}
if new_file {
- let path = self.file.get_path().to_string();
+ let path = self.file.borrow().get_path().to_string();
let _ = Silencer::call(|| {
chmod(&path, 0o600);
Ok(())
@@ -250,7 +257,7 @@ impl JsonConfigSource {
impl ConfigSourceInterface for JsonConfigSource {
fn get_name(&self) -> String {
- self.file.get_path().to_string()
+ self.file.borrow().get_path().to_string()
}
fn add_repository(&mut self, name: &str, config: PhpMixed, append: bool) -> Result<()> {
diff --git a/crates/shirabe/src/dependency_resolver/transaction.rs b/crates/shirabe/src/dependency_resolver/transaction.rs
index 874085f..f9ee5f0 100644
--- a/crates/shirabe/src/dependency_resolver/transaction.rs
+++ b/crates/shirabe/src/dependency_resolver/transaction.rs
@@ -231,10 +231,8 @@ impl Transaction {
// TODO skip updates which don't update? is this needed? we shouldn't schedule this update in the first place?
// if ('update' === $opType) { ... }
- // PHP: return $this->operations = $operations;
- // TODO(phase-b): self.operations assignment plus return — caller needs owned Vec
- self.operations = todo!("operations cloned for both assignment and return");
- todo!("return cloned operations")
+ self.operations = operations.clone();
+ operations
}
/// Determine which packages in the result are not required by any other packages in it.
@@ -339,19 +337,12 @@ impl Transaction {
// is this a plugin with no meaningful dependencies?
if is_downloads_modifying_plugin && requires.is_empty() {
// plugins with no dependencies go to the very front
- // TODO(phase-b): move ownership of operations[idx] into the new vec
- array_unshift(
- &mut dl_modifying_plugins_no_deps,
- todo!("operations[idx] moved out"),
- );
+ array_unshift(&mut dl_modifying_plugins_no_deps, operations[idx].clone());
} else {
// capture the requirements for this package so those packages will be moved up as well
dl_modifying_plugin_requires.extend(requires);
// move the operation to the front
- array_unshift(
- &mut dl_modifying_plugins_with_deps,
- todo!("operations[idx] moved out"),
- );
+ array_unshift(&mut dl_modifying_plugins_with_deps, operations[idx].clone());
}
to_remove.push(idx);
@@ -373,12 +364,12 @@ impl Transaction {
// is this a plugin with no meaningful dependencies?
if is_plugin && requires.is_empty() {
// plugins with no dependencies go to the very front
- array_unshift(&mut plugins_no_deps, todo!("operations[idx] moved out"));
+ array_unshift(&mut plugins_no_deps, operations[idx].clone());
} else {
// capture the requirements for this package so those packages will be moved up as well
plugin_requires.extend(requires);
// move the operation to the front
- array_unshift(&mut plugins_with_deps, todo!("operations[idx] moved out"));
+ array_unshift(&mut plugins_with_deps, operations[idx].clone());
}
to_remove.push(idx);
@@ -424,8 +415,7 @@ impl Transaction {
.downcast_ref::<MarkAliasUninstalledOperation>()
.is_some();
if is_uninstall {
- // TODO(phase-b): move ownership out of operations[idx]
- uninst_ops.push(todo!("operations[idx] moved out"));
+ uninst_ops.push(op.clone());
to_remove.push(idx);
}
}
diff --git a/crates/shirabe/src/downloader/transport_exception.rs b/crates/shirabe/src/downloader/transport_exception.rs
index 9d5b352..c457e1b 100644
--- a/crates/shirabe/src/downloader/transport_exception.rs
+++ b/crates/shirabe/src/downloader/transport_exception.rs
@@ -2,7 +2,7 @@
use shirabe_php_shim::PhpMixed;
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct TransportException {
pub message: String,
pub code: i64,
diff --git a/crates/shirabe/src/factory.rs b/crates/shirabe/src/factory.rs
index cdad33c..95511b0 100644
--- a/crates/shirabe/src/factory.rs
+++ b/crates/shirabe/src/factory.rs
@@ -295,7 +295,10 @@ impl Factory {
config.merge(&read_data, &file_path_owned);
}
// TODO(phase-b): set_config_source takes Box<dyn ConfigSourceInterface>
- config.set_config_source(Box::new(JsonConfigSource::new(file, false)));
+ config.set_config_source(Box::new(JsonConfigSource::new(
+ std::rc::Rc::new(std::cell::RefCell::new(file)),
+ false,
+ )));
let htaccess_protect = config.get("htaccess-protect").as_bool().unwrap_or(false);
if htaccess_protect {
@@ -359,7 +362,10 @@ impl Factory {
config.merge(&wrapped, &auth_path_owned);
}
// TODO(phase-b): set_auth_config_source takes Box<dyn ConfigSourceInterface>
- config.set_auth_config_source(Box::new(JsonConfigSource::new(auth_file, true)));
+ config.set_auth_config_source(Box::new(JsonConfigSource::new(
+ std::rc::Rc::new(std::cell::RefCell::new(auth_file)),
+ true,
+ )));
Self::load_composer_auth_env(&mut config, io)?;
@@ -526,11 +532,11 @@ impl Factory {
crate::io::DEBUG,
);
config.set_config_source(Box::new(JsonConfigSource::new(
- JsonFile::new(
+ std::rc::Rc::new(std::cell::RefCell::new(JsonFile::new(
realpath(composer_file_path).unwrap_or_default(),
None,
Some(io.clone()),
- )?,
+ )?)),
false,
)));
@@ -557,7 +563,7 @@ impl Factory {
let auth_path = local_auth_file.get_path().to_string();
config.merge(&wrapped, &auth_path);
config.set_local_auth_config_source(Box::new(JsonConfigSource::new(
- local_auth_file,
+ std::rc::Rc::new(std::cell::RefCell::new(local_auth_file)),
true,
)));
}
diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs
index ff8a80d..4de52b5 100644
--- a/crates/shirabe/src/installer.rs
+++ b/crates/shirabe/src/installer.rs
@@ -255,9 +255,8 @@ impl Installer {
self.execute_operations = false;
self.write_lock = false;
self.dump_autoloader = false;
- // TODO(phase-b): borrow conflict: passing &mut self.repository_manager while &self
- // is implicit. Refactor mock_local_repositories or split borrow.
- // self.mock_local_repositories(&mut self.repository_manager);
+ let repository_manager = self.repository_manager.clone();
+ self.mock_local_repositories(&mut repository_manager.borrow_mut())?;
}
if self.download_only {
diff --git a/crates/shirabe/src/installer/installer_interface.rs b/crates/shirabe/src/installer/installer_interface.rs
index 20fdb2c..6df00c6 100644
--- a/crates/shirabe/src/installer/installer_interface.rs
+++ b/crates/shirabe/src/installer/installer_interface.rs
@@ -11,19 +11,19 @@ pub trait InstallerInterface: std::fmt::Debug {
fn supports(&self, package_type: &str) -> bool;
fn is_installed(
- &self,
+ &mut self,
repo: &dyn InstalledRepositoryInterface,
package: PackageInterfaceHandle,
) -> bool;
async fn download(
- &self,
+ &mut self,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
) -> anyhow::Result<Option<PhpMixed>>;
async fn prepare(
- &self,
+ &mut self,
r#type: &str,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
@@ -49,13 +49,13 @@ pub trait InstallerInterface: std::fmt::Debug {
) -> anyhow::Result<Option<PhpMixed>>;
async fn cleanup(
- &self,
+ &mut self,
r#type: &str,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
) -> anyhow::Result<Option<PhpMixed>>;
- fn get_install_path(&self, package: PackageInterfaceHandle) -> Option<String>;
+ fn get_install_path(&mut self, package: PackageInterfaceHandle) -> Option<String>;
fn as_binary_presence_interface(&mut self) -> Option<&mut dyn BinaryPresenceInterface> {
None
diff --git a/crates/shirabe/src/installer/library_installer.rs b/crates/shirabe/src/installer/library_installer.rs
index 58706d6..daf5e33 100644
--- a/crates/shirabe/src/installer/library_installer.rs
+++ b/crates/shirabe/src/installer/library_installer.rs
@@ -65,8 +65,7 @@ impl LibraryInstaller {
);
let binary_installer = binary_installer.unwrap_or_else(|| {
BinaryInstaller::new(
- // TODO(phase-b): pass io by reference/clone
- todo!("io reference"),
+ io.clone(),
rtrim(
&composer_ref
.get_config()
@@ -81,8 +80,7 @@ impl LibraryInstaller {
.borrow_mut()
.get_str("bin-compat")
.unwrap_or_default(),
- // TODO(phase-b): pass filesystem reference
- todo!("filesystem reference"),
+ Some(filesystem.clone()),
Some(vendor_dir.clone()),
)
});
@@ -109,7 +107,7 @@ impl LibraryInstaller {
///
/// It is used for BC as getInstallPath tends to be overridden by
/// installer plugins but not getPackageBasePath
- pub(crate) fn get_package_base_path(&self, package: PackageInterfaceHandle) -> String {
+ pub(crate) fn get_package_base_path(&mut self, package: PackageInterfaceHandle) -> String {
let install_path = self.get_install_path(package.clone()).unwrap();
let target_dir = package.get_target_dir();
@@ -133,7 +131,7 @@ impl LibraryInstaller {
/// @return PromiseInterface|null
/// @phpstan-return PromiseInterface<void|null>|null
pub(crate) async fn install_code(
- &self,
+ &mut self,
package: PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
let download_path = self.get_install_path(package.clone()).unwrap();
@@ -147,7 +145,7 @@ impl LibraryInstaller {
/// @return PromiseInterface|null
/// @phpstan-return PromiseInterface<void|null>|null
pub(crate) async fn update_code(
- &self,
+ &mut self,
initial: PackageInterfaceHandle,
target: PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
@@ -178,7 +176,7 @@ impl LibraryInstaller {
/// @return PromiseInterface|null
/// @phpstan-return PromiseInterface<void|null>|null
pub(crate) async fn remove_code(
- &self,
+ &mut self,
package: PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
let download_path = self.get_package_base_path(package.clone());
@@ -226,7 +224,7 @@ impl InstallerInterface for LibraryInstaller {
}
fn is_installed(
- &self,
+ &mut self,
repo: &dyn InstalledRepositoryInterface,
package: PackageInterfaceHandle,
) -> bool {
@@ -256,12 +254,11 @@ impl InstallerInterface for LibraryInstaller {
}
async fn download(
- &self,
+ &mut self,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
) -> Result<Option<PhpMixed>> {
- // TODO(phase-b): initialize_vendor_dir requires &mut self
- // self.initialize_vendor_dir();
+ self.initialize_vendor_dir();
let download_path = self.get_install_path(package.clone()).unwrap();
self.get_download_manager()
@@ -271,13 +268,12 @@ impl InstallerInterface for LibraryInstaller {
}
async fn prepare(
- &self,
+ &mut self,
r#type: &str,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
) -> Result<Option<PhpMixed>> {
- // TODO(phase-b): initialize_vendor_dir requires &mut self
- // self.initialize_vendor_dir();
+ self.initialize_vendor_dir();
let download_path = self.get_install_path(package.clone()).unwrap();
self.get_download_manager()
@@ -287,13 +283,12 @@ impl InstallerInterface for LibraryInstaller {
}
async fn cleanup(
- &self,
+ &mut self,
r#type: &str,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
) -> Result<Option<PhpMixed>> {
- // TODO(phase-b): initialize_vendor_dir requires &mut self
- // self.initialize_vendor_dir();
+ self.initialize_vendor_dir();
let download_path = self.get_install_path(package.clone()).unwrap();
self.get_download_manager()
@@ -307,8 +302,7 @@ impl InstallerInterface for LibraryInstaller {
repo: &mut dyn InstalledRepositoryInterface,
package: PackageInterfaceHandle,
) -> Result<Option<PhpMixed>> {
- // TODO(phase-b): initialize_vendor_dir requires &mut self
- // self.initialize_vendor_dir();
+ self.initialize_vendor_dir();
let download_path = self.get_install_path(package.clone()).unwrap();
// remove the binaries if it appears the package files are missing
@@ -342,8 +336,7 @@ impl InstallerInterface for LibraryInstaller {
.into());
}
- // TODO(phase-b): initialize_vendor_dir requires &mut self
- // self.initialize_vendor_dir();
+ self.initialize_vendor_dir();
self.binary_installer.remove_binaries(initial.clone());
let _ = self.update_code(initial.clone(), target.clone()).await?;
@@ -393,9 +386,8 @@ impl InstallerInterface for LibraryInstaller {
Ok(None)
}
- fn get_install_path(&self, package: PackageInterfaceHandle) -> Option<String> {
- // TODO(phase-b): initialize_vendor_dir requires &mut self
- // self.initialize_vendor_dir();
+ fn get_install_path(&mut self, package: PackageInterfaceHandle) -> Option<String> {
+ self.initialize_vendor_dir();
let base_path = format!(
"{}{}",
diff --git a/crates/shirabe/src/installer/metapackage_installer.rs b/crates/shirabe/src/installer/metapackage_installer.rs
index 0c67e9a..cebec2c 100644
--- a/crates/shirabe/src/installer/metapackage_installer.rs
+++ b/crates/shirabe/src/installer/metapackage_installer.rs
@@ -30,7 +30,7 @@ impl InstallerInterface for MetapackageInstaller {
}
fn is_installed(
- &self,
+ &mut self,
repo: &dyn InstalledRepositoryInterface,
package: PackageInterfaceHandle,
) -> bool {
@@ -38,7 +38,7 @@ impl InstallerInterface for MetapackageInstaller {
}
async fn download(
- &self,
+ &mut self,
_package: PackageInterfaceHandle,
_prev_package: Option<PackageInterfaceHandle>,
) -> Result<Option<PhpMixed>> {
@@ -46,7 +46,7 @@ impl InstallerInterface for MetapackageInstaller {
}
async fn prepare(
- &self,
+ &mut self,
_type: &str,
_package: PackageInterfaceHandle,
_prev_package: Option<PackageInterfaceHandle>,
@@ -55,7 +55,7 @@ impl InstallerInterface for MetapackageInstaller {
}
async fn cleanup(
- &self,
+ &mut self,
_type: &str,
_package: PackageInterfaceHandle,
_prev_package: Option<PackageInterfaceHandle>,
@@ -132,7 +132,7 @@ impl InstallerInterface for MetapackageInstaller {
Ok(None)
}
- fn get_install_path(&self, _package: PackageInterfaceHandle) -> Option<String> {
+ fn get_install_path(&mut self, _package: PackageInterfaceHandle) -> Option<String> {
None
}
}
diff --git a/crates/shirabe/src/installer/noop_installer.rs b/crates/shirabe/src/installer/noop_installer.rs
index 3041e14..9b8aa08 100644
--- a/crates/shirabe/src/installer/noop_installer.rs
+++ b/crates/shirabe/src/installer/noop_installer.rs
@@ -15,7 +15,7 @@ impl InstallerInterface for NoopInstaller {
}
fn is_installed(
- &self,
+ &mut self,
repo: &dyn InstalledRepositoryInterface,
package: PackageInterfaceHandle,
) -> bool {
@@ -23,7 +23,7 @@ impl InstallerInterface for NoopInstaller {
}
async fn download(
- &self,
+ &mut self,
_package: PackageInterfaceHandle,
_prev_package: Option<PackageInterfaceHandle>,
) -> anyhow::Result<Option<PhpMixed>> {
@@ -31,7 +31,7 @@ impl InstallerInterface for NoopInstaller {
}
async fn prepare(
- &self,
+ &mut self,
_type: &str,
_package: PackageInterfaceHandle,
_prev_package: Option<PackageInterfaceHandle>,
@@ -40,7 +40,7 @@ impl InstallerInterface for NoopInstaller {
}
async fn cleanup(
- &self,
+ &mut self,
_type: &str,
_package: PackageInterfaceHandle,
_prev_package: Option<PackageInterfaceHandle>,
@@ -99,7 +99,7 @@ impl InstallerInterface for NoopInstaller {
Ok(None)
}
- fn get_install_path(&self, package: PackageInterfaceHandle) -> Option<String> {
+ fn get_install_path(&mut self, package: PackageInterfaceHandle) -> Option<String> {
let target_dir = package.get_target_dir();
let pretty_name = package.get_pretty_name();
diff --git a/crates/shirabe/src/installer/plugin_installer.rs b/crates/shirabe/src/installer/plugin_installer.rs
index 00b2210..23d7098 100644
--- a/crates/shirabe/src/installer/plugin_installer.rs
+++ b/crates/shirabe/src/installer/plugin_installer.rs
@@ -70,7 +70,7 @@ impl InstallerInterface for PluginInstaller {
}
fn is_installed(
- &self,
+ &mut self,
repo: &dyn InstalledRepositoryInterface,
package: PackageInterfaceHandle,
) -> bool {
@@ -78,7 +78,7 @@ impl InstallerInterface for PluginInstaller {
}
async fn prepare(
- &self,
+ &mut self,
r#type: &str,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
@@ -103,7 +103,7 @@ impl InstallerInterface for PluginInstaller {
}
async fn download(
- &self,
+ &mut self,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
) -> Result<Option<PhpMixed>> {
@@ -166,7 +166,7 @@ impl InstallerInterface for PluginInstaller {
}
async fn cleanup(
- &self,
+ &mut self,
r#type: &str,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
@@ -174,7 +174,7 @@ impl InstallerInterface for PluginInstaller {
self.inner.cleanup(r#type, package, prev_package).await
}
- fn get_install_path(&self, package: PackageInterfaceHandle) -> Option<String> {
+ fn get_install_path(&mut self, package: PackageInterfaceHandle) -> Option<String> {
self.inner.get_install_path(package)
}
diff --git a/crates/shirabe/src/installer/project_installer.rs b/crates/shirabe/src/installer/project_installer.rs
index 6c8a279..351fd35 100644
--- a/crates/shirabe/src/installer/project_installer.rs
+++ b/crates/shirabe/src/installer/project_installer.rs
@@ -36,7 +36,7 @@ impl InstallerInterface for ProjectInstaller {
}
fn is_installed(
- &self,
+ &mut self,
_repo: &dyn InstalledRepositoryInterface,
_package: PackageInterfaceHandle,
) -> bool {
@@ -44,7 +44,7 @@ impl InstallerInterface for ProjectInstaller {
}
async fn download(
- &self,
+ &mut self,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
) -> anyhow::Result<Option<PhpMixed>> {
@@ -69,7 +69,7 @@ impl InstallerInterface for ProjectInstaller {
}
async fn prepare(
- &self,
+ &mut self,
r#type: &str,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
@@ -81,7 +81,7 @@ impl InstallerInterface for ProjectInstaller {
}
async fn cleanup(
- &self,
+ &mut self,
r#type: &str,
package: PackageInterfaceHandle,
prev_package: Option<PackageInterfaceHandle>,
@@ -128,7 +128,7 @@ impl InstallerInterface for ProjectInstaller {
.into())
}
- fn get_install_path(&self, _package: PackageInterfaceHandle) -> Option<String> {
+ fn get_install_path(&mut self, _package: PackageInterfaceHandle) -> Option<String> {
Some(self.install_path.clone())
}
}
diff --git a/crates/shirabe/src/plugin/plugin_manager.rs b/crates/shirabe/src/plugin/plugin_manager.rs
index d4c3113..9dcf754 100644
--- a/crates/shirabe/src/plugin/plugin_manager.rs
+++ b/crates/shirabe/src/plugin/plugin_manager.rs
@@ -126,19 +126,17 @@ impl PluginManager {
pub fn load_installed_plugins(&mut self) -> anyhow::Result<()> {
// TODO(plugin): plugin loading is part of the plugin API
if !self.are_plugins_disabled("local") {
- // TODO(phase-b): PHP returns a shared object reference; we clone the repository
- // box here to side-step a borrow conflict between `&self.composer` and
- // `&mut self`. The Rust port should eventually share via Rc<RefCell<_>>.
let repo = self
.composer_full()
.borrow()
.get_repository_manager()
.borrow()
.get_local_repository();
- let root_package = crate::package::RootPackageInterfaceHandle::dup(
- self.composer_full().borrow().get_package(),
- );
- self.load_repository(&mut *repo.borrow_mut(), false, Some(root_package))?;
+ self.load_repository(
+ &mut *repo.borrow_mut(),
+ false,
+ Some(self.composer_full().borrow().get_package().clone()),
+ )?;
}
if self.global_composer.is_some() && !self.are_plugins_disabled("global") {
diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs
index f194e69..a35874d 100644
--- a/crates/shirabe/src/repository/platform_repository.rs
+++ b/crates/shirabe/src/repository/platform_repository.rs
@@ -1641,14 +1641,14 @@ impl PlatformRepository {
name: self.overrides["php"].name.clone(),
version: self.overrides["php"].version.clone(),
};
- let mut overrider =
+ let overrider =
self.add_overridden_package(&php_override, Some(package.get_pretty_name()))?;
let actual_text = if package.get_version() == overrider.get_version() {
"same as actual".to_string()
} else {
format!("actual: {}", package.get_pretty_version())
};
- let current_description = overrider.get_description().unwrap_or("").to_string();
+ let current_description = overrider.get_description().unwrap_or_default();
overrider.set_description(format!("{}, {}", current_description, actual_text));
return Ok(());
@@ -1662,7 +1662,7 @@ impl PlatformRepository {
&mut self,
r#override: &PlatformOverride,
name: Option<String>,
- ) -> anyhow::Result<CompletePackage> {
+ ) -> anyhow::Result<CompletePackageHandle> {
let version_str = match &r#override.version {
PhpMixed::String(s) => s.clone(),
_ => "".to_string(),
@@ -1681,14 +1681,11 @@ impl PlatformRepository {
let mut extra: IndexMap<String, PhpMixed> = IndexMap::new();
extra.insert("config.platform".to_string(), PhpMixed::Bool(true));
package.inner.set_extra(extra);
- // TODO(phase-b): CompletePackage is a PHP class (shared by ref); cannot Clone.
- // The container should likely store Rc<RefCell<CompletePackage>> so both the inner
- // ArrayRepository and the function caller can share ownership.
- let _: () = todo!("share CompletePackage via Rc between add_package and return");
+ let package = CompletePackageHandle::from_complete_package(package);
+ self.add_package(package.clone().into())?;
- #[allow(unreachable_code)]
if package.get_name() == "php" {
- let parts = explode(".", package.get_version());
+ let parts = explode(".", &package.get_version());
let head = array_slice_strs(&parts, 0, Some(3));
*LAST_SEEN_PLATFORM_PHP.lock().unwrap() = Some(implode(".", &head));
}
diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs
index 3cc4b9f..367a46a 100644
--- a/crates/shirabe/src/repository/repository_set.rs
+++ b/crates/shirabe/src/repository/repository_set.rs
@@ -459,7 +459,7 @@ impl RepositorySet {
/// @param list<string>|null $allowedTypes Only packages of those types are allowed if set to non-null
pub fn create_pool(
&mut self,
- request: Request,
+ mut request: Request,
io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>,
event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
pool_optimizer: Option<PoolOptimizer>,
@@ -467,17 +467,34 @@ impl RepositorySet {
allowed_types: Option<Vec<String>>,
security_advisory_pool_filter: Option<SecurityAdvisoryPoolFilter>,
) -> Result<Pool> {
+ let root_aliases = self
+ .root_aliases
+ .iter()
+ .map(|(name, versions)| {
+ let versions = versions
+ .iter()
+ .map(|(version, entry)| {
+ let mut fields = IndexMap::new();
+ fields.insert("alias".to_string(), entry.alias.clone());
+ fields.insert(
+ "alias_normalized".to_string(),
+ entry.alias_normalized.clone(),
+ );
+ (version.clone(), fields)
+ })
+ .collect();
+ (name.clone(), versions)
+ })
+ .collect();
let mut pool_builder = PoolBuilder::new(
self.acceptable_stabilities.clone(),
self.stability_flags.clone(),
- // TODO(phase-b): clone root_aliases into PoolBuilder's expected type
- todo!("self.root_aliases.clone()"),
+ root_aliases,
self.root_references.clone(),
io,
event_dispatcher,
pool_optimizer,
- // TODO(phase-b): clone temporary_constraints
- todo!("self.temporary_constraints.clone()"),
+ self.temporary_constraints.clone(),
security_advisory_pool_filter,
);
pool_builder.set_ignored_types(ignored_types);
@@ -501,11 +518,7 @@ impl RepositorySet {
self.locked = true;
- // TODO(phase-b): pool_builder.build_pool takes owned Vec and &mut Request; revisit sharing model
- pool_builder.build_pool(
- todo!("share self.repositories"),
- todo!("share request as &mut"),
- )
+ pool_builder.build_pool(self.repositories.clone(), &mut request)
}
/// Create a pool for dependency resolution from the packages in this repository set.
diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs
index 3d344ec..9b2ea47 100644
--- a/crates/shirabe/src/repository/vcs/svn_driver.rs
+++ b/crates/shirabe/src/repository/vcs/svn_driver.rs
@@ -85,8 +85,7 @@ impl SvnDriver {
}
self.inner.cache = Some(Cache::new(
- // TODO(phase-b): pass io by reference/clone
- todo!("self.inner.io clone"),
+ self.inner.io.clone(),
&format!(
"{}/{}",
self.inner
@@ -551,10 +550,9 @@ impl SvnDriver {
if self.util.is_none() {
self.util = Some(SvnUtil::new(
self.base_url.clone(),
- // TODO(phase-b): clone or borrow io/config
- todo!("self.inner.io clone"),
- todo!("self.inner.config clone"),
- Some(todo!("self.inner.process clone")),
+ self.inner.io.clone(),
+ self.inner.config.clone(),
+ Some(self.inner.process.clone()),
));
self.util
.as_mut()
diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs
index e90ac62..4c2492a 100644
--- a/crates/shirabe/src/repository/vcs_repository.rs
+++ b/crates/shirabe/src/repository/vcs_repository.rs
@@ -70,9 +70,7 @@ pub struct VcsRepository {
/// @var list<string>
empty_references: Vec<String>,
/// @var array<'tags'|'branches', array<string, TransportException>>
- // TODO(phase-b): TransportException is a PHP class; uses Rc<T> for shared ownership.
- version_transport_exceptions:
- IndexMap<String, IndexMap<String, std::rc::Rc<TransportException>>>,
+ version_transport_exceptions: IndexMap<String, IndexMap<String, TransportException>>,
/// @var ?EventDispatcher (preserved for plugin events)
_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
}
@@ -277,7 +275,7 @@ impl VcsRepository {
/// @return array<'tags'|'branches', array<string, TransportException>>
pub fn get_version_transport_exceptions(
&self,
- ) -> &IndexMap<String, IndexMap<String, std::rc::Rc<TransportException>>> {
+ ) -> &IndexMap<String, IndexMap<String, TransportException>> {
&self.version_transport_exceptions
}
@@ -543,15 +541,10 @@ impl VcsRepository {
})();
if let Err(e) = result {
if let Some(te) = e.downcast_ref::<TransportException>() {
- // TODO(phase-b): TransportException is a PHP class (shared by ref). We only
- // have &TransportException from downcast_ref; obtaining the Rc requires the
- // anyhow::Error chain to carry an Rc. For now we insert a todo!() placeholder.
- let shared_te: std::rc::Rc<TransportException> =
- todo!("share TransportException via Rc through anyhow::Error chain");
self.version_transport_exceptions
.entry("tags".to_string())
.or_insert_with(IndexMap::new)
- .insert(tag.clone(), shared_te);
+ .insert(tag.clone(), te.clone());
if te.get_code() == 404 {
self.empty_references.push(identifier.clone());
}
@@ -728,14 +721,10 @@ impl VcsRepository {
})();
if let Err(e) = result {
if let Some(te) = e.downcast_ref::<TransportException>() {
- // TODO(phase-b): TransportException is a PHP class (shared by ref).
- // See the matching tags block above; same Rc story applies.
- let shared_te: std::rc::Rc<TransportException> =
- todo!("share TransportException via Rc through anyhow::Error chain");
self.version_transport_exceptions
.entry("branches".to_string())
.or_insert_with(IndexMap::new)
- .insert(branch.clone(), shared_te);
+ .insert(branch.clone(), te.clone());
if te.get_code() == 404 {
self.empty_references.push(identifier.clone());
}
diff --git a/crates/shirabe/src/util/auth_helper.rs b/crates/shirabe/src/util/auth_helper.rs
index 61970f2..2709751 100644
--- a/crates/shirabe/src/util/auth_helper.rs
+++ b/crates/shirabe/src/util/auth_helper.rs
@@ -147,13 +147,7 @@ impl AuthHelper {
.any(|v| v.as_string() == Some(origin));
if in_github_domains {
- let mut git_hub_util = GitHub::new(
- // TODO(phase-b): clone or borrow io/config rather than moving
- todo!("io clone"),
- todo!("config clone"),
- None,
- None,
- )?;
+ let mut git_hub_util = GitHub::new(self.io.clone(), self.config.clone(), None, None)?;
let mut message = "\n".to_string();
let rate_limited = git_hub_util.is_rate_limited(&headers);
@@ -258,13 +252,7 @@ impl AuthHelper {
"to go over the API rate limit"
},
);
- let mut git_lab_util = GitLab::new(
- // TODO(phase-b): clone or borrow io/config rather than moving
- todo!("io clone"),
- todo!("config clone"),
- None,
- None,
- )?;
+ let mut git_lab_util = GitLab::new(self.io.clone(), self.config.clone(), None, None)?;
let mut auth: Option<IndexMap<String, Option<String>>> = None;
if self.io.has_authentication(origin) {
@@ -330,14 +318,8 @@ impl AuthHelper {
.and_then(|v| v.clone())
.unwrap_or_default();
if username != "x-token-auth" {
- let mut bitbucket_util = Bitbucket::new(
- // TODO(phase-b): clone or borrow io/config rather than moving
- todo!("io clone"),
- todo!("config clone"),
- None,
- None,
- None,
- )?;
+ let mut bitbucket_util =
+ Bitbucket::new(self.io.clone(), self.config.clone(), None, None, None)?;
let password = auth
.get("password")
.and_then(|v| v.clone())
@@ -377,14 +359,8 @@ impl AuthHelper {
"go over the API rate limit"
},
);
- let mut bit_bucket_util = Bitbucket::new(
- // TODO(phase-b): clone or borrow io/config rather than moving
- todo!("io clone"),
- todo!("config clone"),
- None,
- None,
- None,
- )?;
+ let mut bit_bucket_util =
+ Bitbucket::new(self.io.clone(), self.config.clone(), None, None, None)?;
if !bit_bucket_util.authorize_oauth(&origin)
&& (!self.io.is_interactive()
|| !bit_bucket_util
@@ -506,7 +482,7 @@ impl AuthHelper {
}
// PHP: $headers = &$options['http']['header'];
- // TODO(phase-b): captured by reference; pushes below modify the same list
+ // The reference is emulated by writing `headers` back into options below.
let mut headers: Vec<PhpMixed> = match options
.get("http")
.and_then(|v| v.as_array())
diff --git a/crates/shirabe/src/util/forgejo.rs b/crates/shirabe/src/util/forgejo.rs
index 232c8f3..974fb4a 100644
--- a/crates/shirabe/src/util/forgejo.rs
+++ b/crates/shirabe/src/util/forgejo.rs
@@ -143,8 +143,6 @@ impl Forgejo {
}
// store value in local/user config
- // TODO(phase-b): Config getters return references; cross-borrows of self.config.borrow()
- // cannot live across method calls. Needs Rc<RefCell<dyn ConfigSourceInterface>> shape.
let setting_key = format!("forgejo-token.{}", origin_url);
{
let mut cfg = self.config.borrow_mut();
diff --git a/crates/shirabe/src/util/github.rs b/crates/shirabe/src/util/github.rs
index 18958ca..26544cb 100644
--- a/crates/shirabe/src/util/github.rs
+++ b/crates/shirabe/src/util/github.rs
@@ -261,8 +261,6 @@ impl GitHub {
}
}
- // TODO(phase-b): Config getters return references; cross-borrow chains require
- // Rc<RefCell<dyn ConfigSourceInterface>> shape. For now use _mut variants.
let use_local = store_in_local_auth_config
&& self
.config
diff --git a/crates/shirabe/src/util/http/curl_downloader.rs b/crates/shirabe/src/util/http/curl_downloader.rs
index 9d572fa..c9eb2f8 100644
--- a/crates/shirabe/src/util/http/curl_downloader.rs
+++ b/crates/shirabe/src/util/http/curl_downloader.rs
@@ -197,9 +197,7 @@ impl CurlDownloader {
share_handle = Some(sh);
}
- // TODO(phase-b): clone io/config for AuthHelper construction without consuming.
- let auth_helper =
- AuthHelper::new(unsafe { std::mem::zeroed() }, unsafe { std::mem::zeroed() });
+ let auth_helper = AuthHelper::new(io.clone(), config.clone());
let mut multi_errors: IndexMap<i64, Vec<String>> = IndexMap::new();
multi_errors.insert(