aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-10 02:41:34 +0900
committernsfisis <nsfisis@gmail.com>2026-06-10 02:46:18 +0900
commit2d474e91e49c7343d28198eff2b5bbbed9afbcee (patch)
tree8c1ab321dfa5ddc1ca9d2871eb06a6fde6b2970b /crates
parente583112899cbea7494ffdd73d7de380dd5f808c4 (diff)
downloadphp-shirabe-2d474e91e49c7343d28198eff2b5bbbed9afbcee.tar.gz
php-shirabe-2d474e91e49c7343d28198eff2b5bbbed9afbcee.tar.zst
php-shirabe-2d474e91e49c7343d28198eff2b5bbbed9afbcee.zip
feat(phase-c): resolve cross-module phase-b TODOs
Diffstat (limited to 'crates')
-rw-r--r--crates/shirabe-php-shim/src/lib.rs13
-rw-r--r--crates/shirabe/src/autoload/autoload_generator.rs18
-rw-r--r--crates/shirabe/src/command/audit_command.rs2
-rw-r--r--crates/shirabe/src/command/base_command.rs5
-rw-r--r--crates/shirabe/src/command/base_dependency_command.rs11
-rw-r--r--crates/shirabe/src/command/create_project_command.rs18
-rw-r--r--crates/shirabe/src/command/package_discovery_trait.rs8
-rw-r--r--crates/shirabe/src/command/remove_command.rs8
-rw-r--r--crates/shirabe/src/command/require_command.rs11
-rw-r--r--crates/shirabe/src/command/script_alias_command.rs4
-rw-r--r--crates/shirabe/src/command/show_command.rs12
-rw-r--r--crates/shirabe/src/command/update_command.rs44
-rw-r--r--crates/shirabe/src/dependency_resolver/decisions.rs34
-rw-r--r--crates/shirabe/src/dependency_resolver/problem.rs21
-rw-r--r--crates/shirabe/src/dependency_resolver/request.rs8
-rw-r--r--crates/shirabe/src/dependency_resolver/rule.rs8
-rw-r--r--crates/shirabe/src/dependency_resolver/solver.rs18
-rw-r--r--crates/shirabe/src/downloader/file_downloader.rs7
-rw-r--r--crates/shirabe/src/downloader/git_downloader.rs3
-rw-r--r--crates/shirabe/src/event_dispatcher/event_dispatcher.rs6
-rw-r--r--crates/shirabe/src/factory.rs14
-rw-r--r--crates/shirabe/src/installer.rs59
-rw-r--r--crates/shirabe/src/installer/installation_manager.rs2
-rw-r--r--crates/shirabe/src/io/buffer_io.rs7
-rw-r--r--crates/shirabe/src/package/version/version_guesser.rs2
-rw-r--r--crates/shirabe/src/repository/array_repository.rs5
-rw-r--r--crates/shirabe/src/repository/filesystem_repository.rs3
-rw-r--r--crates/shirabe/src/repository/repository_factory.rs4
-rw-r--r--crates/shirabe/src/repository/repository_set.rs1
-rw-r--r--crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs12
-rw-r--r--crates/shirabe/src/repository/vcs/svn_driver.rs17
-rw-r--r--crates/shirabe/src/repository/vcs/vcs_driver.rs1
-rw-r--r--crates/shirabe/src/repository/writable_array_repository.rs4
-rw-r--r--crates/shirabe/src/util/http_downloader.rs3
-rw-r--r--crates/shirabe/src/util/platform.rs22
-rw-r--r--crates/shirabe/src/util/remote_filesystem.rs6
36 files changed, 184 insertions, 237 deletions
diff --git a/crates/shirabe-php-shim/src/lib.rs b/crates/shirabe-php-shim/src/lib.rs
index f47e221..873f7f4 100644
--- a/crates/shirabe-php-shim/src/lib.rs
+++ b/crates/shirabe-php-shim/src/lib.rs
@@ -2477,3 +2477,16 @@ pub fn date_format_to_strftime(format: &str) -> &'static str {
other => panic!("Unsupported PHP date format: {other:?}"),
}
}
+
+// NOTE: &str matching in const expression does not compile for now.
+pub const PHP_OS: &str = match std::env::consts::OS.as_bytes() {
+ b"linux" => "Linux",
+ b"macos" => "Darwin",
+ b"windows" => "WINNT",
+ b"freebsd" => "FreeBSD",
+ b"openbsd" => "OpenBSD",
+ b"netbsd" => "NetBSD",
+ b"dragonfly" => "DragonFly",
+ b"solaris" | b"illumos" => "SunOS",
+ _ => std::env::consts::OS,
+};
diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs
index 69ecb73..a380763 100644
--- a/crates/shirabe/src/autoload/autoload_generator.rs
+++ b/crates/shirabe/src/autoload/autoload_generator.rs
@@ -438,8 +438,11 @@ impl AutoloadGenerator {
}
let mut class_map = class_map_generator.take_class_map();
- // TODO(phase-b): strict_ambiguous should filter vendor path for non-strict mode
- let ambiguous_classes = class_map.get_ambiguous_classes(None)?;
+ let ambiguous_classes = if strict_ambiguous {
+ class_map.get_ambiguous_classes(None)?
+ } else {
+ class_map.get_ambiguous_classes(Some(r"{/(test|fixture|example|stub)s?/}i"))?
+ };
for (class_name, ambiguous_paths) in &ambiguous_classes {
if ambiguous_paths.len() > 1 {
self.io.write_error(&format!(
@@ -804,14 +807,17 @@ impl AutoloadGenerator {
};
let mut sorted_package_map = self.sort_package_map(package_map);
sorted_package_map.push(root_package_map);
+ let reverse_sorted_map: Vec<(PackageInterfaceHandle, Option<String>)> =
+ sorted_package_map.iter().rev().cloned().collect();
- // TODO(phase-b): psr-0/4/classmap should use reverse_sorted_map (root first) for correct precedence
+ // reverse-sorted means root first, then dependents, then their dependents, etc.
+ // which makes sense to allow root to override classmap or psr-0/4 entries with higher precedence rules
let mut psr0 =
- self.parse_autoloads_type(&sorted_package_map, "psr-0", root_package.clone());
+ self.parse_autoloads_type(&reverse_sorted_map, "psr-0", root_package.clone());
let mut psr4 =
- self.parse_autoloads_type(&sorted_package_map, "psr-4", root_package.clone());
+ self.parse_autoloads_type(&reverse_sorted_map, "psr-4", root_package.clone());
let classmap =
- self.parse_autoloads_type(&sorted_package_map, "classmap", root_package.clone());
+ self.parse_autoloads_type(&reverse_sorted_map, "classmap", root_package.clone());
// sorted (i.e. dependents first) for files to ensure that dependencies are loaded/available once a file is included
let files = self.parse_autoloads_type(&sorted_package_map, "files", root_package.clone());
diff --git a/crates/shirabe/src/command/audit_command.rs b/crates/shirabe/src/command/audit_command.rs
index 20532f1..0e050cc 100644
--- a/crates/shirabe/src/command/audit_command.rs
+++ b/crates/shirabe/src/command/audit_command.rs
@@ -181,7 +181,7 @@ impl AuditCommand {
}
let _root_pkg = composer.get_package();
- // TODO(phase-b): InstalledRepository::new expects Vec<Box<dyn RepositoryInterface>>, but
+ // TODO(phase-c): InstalledRepository::new expects Vec<Box<dyn RepositoryInterface>>, but
// get_local_repository returns &dyn InstalledRepositoryInterface. Conversion requires
// either cloning into a Box or restructuring InstalledRepository constructor.
let _ = RepositoryUtils::filter_required_packages;
diff --git a/crates/shirabe/src/command/base_command.rs b/crates/shirabe/src/command/base_command.rs
index c98bc2b..b1a1837 100644
--- a/crates/shirabe/src/command/base_command.rs
+++ b/crates/shirabe/src/command/base_command.rs
@@ -511,9 +511,8 @@ impl<C: HasBaseCommandData> BaseCommand for C {
} else {
crate::factory::DisablePlugins::None
};
- // TODO(phase-b): Option<IndexMap<String, PhpMixed>> -> Option<LocalConfigInput> conversion
- let _ = config;
- Factory::create(io, None, disable_plugins_kind, disable_scripts).map(|c| c.upcast())
+ let config = config.map(crate::factory::LocalConfigInput::Data);
+ Factory::create(io, config, disable_plugins_kind, disable_scripts).map(|c| c.upcast())
}
fn get_preferred_install_options(
diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs
index 9317b31..decfd5b 100644
--- a/crates/shirabe/src/command/base_dependency_command.rs
+++ b/crates/shirabe/src/command/base_dependency_command.rs
@@ -106,17 +106,18 @@ pub trait BaseDependencyCommand: BaseCommand {
repos.push(local_repo);
- let platform_overrides = composer
+ let platform_overrides: IndexMap<String, PhpMixed> = composer
.get_config()
.borrow()
.get("platform")
.as_array()
.cloned()
- .unwrap_or_default();
- // TODO(phase-b): platform_overrides type adjustment; using empty for now
- let _ = platform_overrides;
+ .unwrap_or_default()
+ .into_iter()
+ .map(|(k, v)| (k, *v))
+ .collect();
repos.push(crate::repository::RepositoryInterfaceHandle::new(
- PlatformRepository::new(vec![], IndexMap::new())?,
+ PlatformRepository::new(vec![], platform_overrides)?,
));
}
diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs
index c49d2fc..bf00371 100644
--- a/crates/shirabe/src/command/create_project_command.rs
+++ b/crates/shirabe/src/command/create_project_command.rs
@@ -293,7 +293,7 @@ impl CreateProjectCommand {
io.clone(),
&config,
package_name,
- &*platform_requirement_filter,
+ platform_requirement_filter.clone(),
directory.clone(),
package_version,
stability,
@@ -334,17 +334,10 @@ impl CreateProjectCommand {
.get_config()
.borrow()
.get_repositories();
- // TODO(phase-b): generate_repository_name expects existing repos as
- // IndexMap<String, Box<dyn RepositoryInterface>>; pass empty placeholder.
- let _ = &composer_json_repositories_config;
- let placeholder_existing: IndexMap<
- String,
- crate::repository::RepositoryInterfaceHandle,
- > = IndexMap::new();
let name = RepositoryFactory::generate_repository_name(
&PhpMixed::Int(index as i64),
&repo_config,
- &placeholder_existing,
+ &composer_json_repositories_config,
);
let mut config_source = JsonConfigSource::new(
std::rc::Rc::new(std::cell::RefCell::new(JsonFile::new(
@@ -595,7 +588,7 @@ impl CreateProjectCommand {
io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>,
config: &std::rc::Rc<std::cell::RefCell<Config>>,
package_name: &str,
- platform_requirement_filter: &dyn PlatformRequirementFilterInterface,
+ platform_requirement_filter: std::rc::Rc<dyn PlatformRequirementFilterInterface>,
directory: Option<String>,
mut package_version: Option<String>,
mut stability: Option<String>,
@@ -850,14 +843,11 @@ impl CreateProjectCommand {
std::rc::Rc::new(std::cell::RefCell::new(repository_set)),
Some(&mut platform_repo),
)?;
- // TODO(phase-b): platform_requirement_filter is &dyn here but VersionSelector expects
- // Option<Box<dyn ...>>; pass None as placeholder.
- let _ = platform_requirement_filter;
let package = version_selector.find_best_candidate(
&name,
package_version.as_deref(),
&stability,
- None,
+ Some(platform_requirement_filter.clone()),
0,
Some(io.clone()),
PhpMixed::Bool(true),
diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs
index 32e5e21..b5e6cd5 100644
--- a/crates/shirabe/src/command/package_discovery_trait.rs
+++ b/crates/shirabe/src/command/package_discovery_trait.rs
@@ -852,7 +852,6 @@ pub trait PackageDiscoveryTrait {
let installed_repo = repository_manager.get_local_repository();
for result in &results {
- // TODO(phase-b): installed_repo.find_package signature mismatch with FindPackageConstraint
if installed_repo
.find_package(
&result.name,
@@ -929,11 +928,14 @@ pub trait PackageDiscoveryTrait {
let has_config_platform = platform_extra.contains_key("config.platform");
let is_complete = platform_pkg.as_complete().is_some();
if has_config_platform && is_complete {
- // TODO(phase-b): platform_pkg.get_description() via CompletePackageInterface
platform_pkg_version = format!(
"{} ({})",
platform_pkg_version,
- todo!("platform_pkg.get_description()")
+ platform_pkg
+ .as_complete()
+ .unwrap()
+ .get_description()
+ .unwrap_or_default()
);
}
details.push(format!(
diff --git a/crates/shirabe/src/command/remove_command.rs b/crates/shirabe/src/command/remove_command.rs
index 1857b17..6cd2424 100644
--- a/crates/shirabe/src/command/remove_command.rs
+++ b/crates/shirabe/src/command/remove_command.rs
@@ -14,6 +14,7 @@ use crate::config::JsonConfigSource;
use crate::console::input::InputArgument;
use crate::console::input::InputOption;
use crate::dependency_resolver::Request;
+use crate::dependency_resolver::UpdateAllowTransitiveDeps;
use crate::factory::Factory;
use crate::installer::Installer;
use crate::io::IOInterface;
@@ -597,7 +598,7 @@ impl RemoveCommand {
.unwrap_or(false);
let mut update_allow_transitive_dependencies =
- Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
+ UpdateAllowTransitiveDeps::UpdateListedWithTransitiveDepsNoRootRequire;
let mut flags = String::new();
if input
.borrow()
@@ -610,7 +611,8 @@ impl RemoveCommand {
.as_bool()
.unwrap_or(false)
{
- update_allow_transitive_dependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
+ update_allow_transitive_dependencies =
+ UpdateAllowTransitiveDeps::UpdateListedWithTransitiveDeps;
flags += " --with-all-dependencies";
} else if input
.borrow()
@@ -618,7 +620,7 @@ impl RemoveCommand {
.as_bool()
.unwrap_or(false)
{
- update_allow_transitive_dependencies = Request::UPDATE_ONLY_LISTED;
+ update_allow_transitive_dependencies = UpdateAllowTransitiveDeps::UpdateOnlyListed;
flags += " --with-dependencies";
}
diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs
index 7fa4793..44ab921 100644
--- a/crates/shirabe/src/command/require_command.rs
+++ b/crates/shirabe/src/command/require_command.rs
@@ -20,6 +20,7 @@ use crate::composer::PartialComposerHandle;
use crate::console::input::InputArgument;
use crate::console::input::InputOption;
use crate::dependency_resolver::Request;
+use crate::dependency_resolver::UpdateAllowTransitiveDeps;
use crate::factory::Factory;
use crate::installer::Installer;
use crate::installer::InstallerEvents;
@@ -848,7 +849,7 @@ impl RequireCommand {
.as_bool()
.unwrap_or(false);
- let mut update_allow_transitive_dependencies = Request::UPDATE_ONLY_LISTED;
+ let mut update_allow_transitive_dependencies = UpdateAllowTransitiveDeps::UpdateOnlyListed;
let mut flags = String::new();
if input
.borrow()
@@ -861,7 +862,8 @@ impl RequireCommand {
.as_bool()
.unwrap_or(false)
{
- update_allow_transitive_dependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
+ update_allow_transitive_dependencies =
+ UpdateAllowTransitiveDeps::UpdateListedWithTransitiveDeps;
flags += " --with-all-dependencies";
} else if input
.borrow()
@@ -875,7 +877,7 @@ impl RequireCommand {
.unwrap_or(false)
{
update_allow_transitive_dependencies =
- Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
+ UpdateAllowTransitiveDeps::UpdateListedWithTransitiveDepsNoRootRequire;
flags += " --with-dependencies";
}
@@ -1135,7 +1137,8 @@ impl RequireCommand {
IndexMap::new(),
);
let stability_flags_clone = stability_flags.clone();
- // TODO(phase-b): get_locker_mut needs update_hash with stability flags rewriter.
+ // TODO(phase-c): Locker::update_hash needs the stability-flags rewriter callback;
+ // depends on modeling the closure passed to updateHash.
let _ = &stability_flags_clone;
todo!("update locker hash with stability flags rewriter");
}
diff --git a/crates/shirabe/src/command/script_alias_command.rs b/crates/shirabe/src/command/script_alias_command.rs
index 50e7e80..ef95c7e 100644
--- a/crates/shirabe/src/command/script_alias_command.rs
+++ b/crates/shirabe/src/command/script_alias_command.rs
@@ -103,7 +103,7 @@ impl ScriptAliasCommand {
let args = input.borrow().get_arguments();
- // TODO(phase-b): InputInterface has_to_string/get_class_name not modeled in Rust
+ // TODO(phase-c): InputInterface has_to_string/get_class_name not modeled in Rust
// TODO remove for Symfony 6+ as it is then in the interface
if false {
return Err(LogicException {
@@ -122,7 +122,7 @@ impl ScriptAliasCommand {
Platform::put_env("COMPOSER_DEV_MODE", if dev_mode { "1" } else { "0" });
- // TODO(phase-b): InputInterface lacks to_string; use a placeholder
+ // TODO(phase-c): InputInterface lacks to_string; use a placeholder until it is modeled.
let input_as_string = String::new();
let _ = input;
let script_alias_input = Preg::replace4(r"{^\S+ ?}", "", &input_as_string, 1)?;
diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs
index 1ce91cb..3761b09 100644
--- a/crates/shirabe/src/command/show_command.rs
+++ b/crates/shirabe/src/command/show_command.rs
@@ -610,7 +610,7 @@ impl ShowCommand {
.get_option("patch-only")
.as_bool()
.unwrap_or(false),
- &*platform_req_filter,
+ platform_req_filter.clone(),
)?;
}
if input.borrow().get_option("outdated").as_bool() == Some(true)
@@ -885,7 +885,7 @@ impl ShowCommand {
show_major_only,
show_minor_only,
show_patch_only,
- &*platform_req_filter,
+ platform_req_filter.clone(),
)?;
if latest.is_none() {
continue;
@@ -2639,7 +2639,7 @@ impl ShowCommand {
major_only: bool,
minor_only: bool,
patch_only: bool,
- platform_req_filter: &dyn PlatformRequirementFilterInterface,
+ platform_req_filter: std::rc::Rc<dyn PlatformRequirementFilterInterface>,
) -> anyhow::Result<Option<crate::package::PackageInterfaceHandle>> {
// find the latest version allowed in this repo set
let name = package.get_name();
@@ -2738,14 +2738,14 @@ impl ShowCommand {
version_compare(&candidate.get_version(), &package_version, "<=")
});
}
- // TODO(phase-b): platform_req_filter needs to be Option<Box<dyn ...>>; current code holds &dyn.
- let _ = platform_req_filter;
+ // TODO(phase-c): PHP passes $showWarnings (true or a closure) as the last argument, but the
+ // closure form requires modeling a callable inside PhpMixed; hardcoding true until then.
let _ = show_warnings_box;
let mut candidate = version_selector.find_best_candidate(
&name,
target_version.as_deref(),
&best_stability,
- None,
+ Some(platform_req_filter),
0,
Some(self.get_io().clone()),
PhpMixed::Bool(true),
diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs
index 751203c..eb618ca 100644
--- a/crates/shirabe/src/command/update_command.rs
+++ b/crates/shirabe/src/command/update_command.rs
@@ -49,7 +49,8 @@ impl UpdateCommand {
.set_name("update")
.set_aliases(&["u".to_string(), "upgrade".to_string()])
.set_description("Updates your dependencies to the latest version according to composer.json, and updates the composer.lock file")
- // TODO(phase-b): set_definition with InputArgument/InputOption (see PHP UpdateCommand)
+ // TODO(phase-c): populate with InputArgument/InputOption entries (see PHP UpdateCommand);
+ // blocked on the symfony InputDefinition entry modeling.
.set_definition(&[])
.set_help(
"The <info>update</info> command reads the composer.json file from the\n\
@@ -227,15 +228,10 @@ impl UpdateCommand {
"~{}",
matches.get(1).cloned().unwrap_or_default()
))?;
- if temporary_constraints.contains_key(&package.get_name()) {
- let existing = temporary_constraints
- .get(&package.get_name())
- .map(|c| c.clone())
- .unwrap();
+ if let Some(existing) = temporary_constraints.get(&package.get_name()) {
temporary_constraints.insert(
package.get_name(),
- // TODO(phase-b): MultiConstraint::create signature
- todo!("MultiConstraint::create([existing, constraint], true)"),
+ MultiConstraint::create(vec![existing.clone(), constraint], true, None)?,
);
} else {
temporary_constraints.insert(package.get_name(), constraint);
@@ -376,14 +372,15 @@ impl UpdateCommand {
.as_bool()
.unwrap_or(false);
- let mut update_allow_transitive_dependencies: i64 = Request::UPDATE_ONLY_LISTED;
+ let mut update_allow_transitive_dependencies = UpdateAllowTransitiveDeps::UpdateOnlyListed;
if input
.borrow()
.get_option("with-all-dependencies")
.as_bool()
.unwrap_or(false)
{
- update_allow_transitive_dependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
+ update_allow_transitive_dependencies =
+ UpdateAllowTransitiveDeps::UpdateListedWithTransitiveDeps;
} else if input
.borrow()
.get_option("with-dependencies")
@@ -391,10 +388,8 @@ impl UpdateCommand {
.unwrap_or(false)
{
update_allow_transitive_dependencies =
- Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
+ UpdateAllowTransitiveDeps::UpdateListedWithTransitiveDepsNoRootRequire;
}
- // Keep `UpdateAllowTransitiveDeps` import alive while still using i64 for the setter.
- let _ = UpdateAllowTransitiveDeps::UpdateOnlyListed;
install
.set_dry_run(
@@ -456,13 +451,7 @@ impl UpdateCommand {
.as_bool()
.unwrap_or(false),
)
- // TODO(phase-b): VersionParser::parse_constraints returns Arc<dyn ...> but
- // Installer::set_temporary_constraints expects IndexMap<String, Box<dyn ...>>;
- // bridge the constraint storage types later.
- .set_temporary_constraints({
- let _ = &temporary_constraints;
- IndexMap::new()
- })
+ .set_temporary_constraints(temporary_constraints)
.set_audit_config(
self.create_audit_config(&mut *composer.get_config().borrow_mut(), input.clone())?,
)
@@ -548,7 +537,6 @@ impl UpdateCommand {
);
let filter: Option<String> = if packages.len() > 0 {
- // TODO(phase-b): base_package::package_names_to_regexp signature
Some(base_package::package_names_to_regexp(&packages, "%s"))
} else {
None
@@ -682,11 +670,21 @@ impl UpdateCommand {
fn create_version_selector(&self, composer: &PartialComposerHandle) -> Result<VersionSelector> {
let composer = crate::command::composer_full(composer);
+ let root_aliases: Vec<crate::repository::RootAliasInput> = composer
+ .get_package()
+ .get_aliases()
+ .into_iter()
+ .map(|alias| crate::repository::RootAliasInput {
+ package: alias.get("package").cloned().unwrap_or_default(),
+ version: alias.get("version").cloned().unwrap_or_default(),
+ alias: alias.get("alias").cloned().unwrap_or_default(),
+ alias_normalized: alias.get("alias_normalized").cloned().unwrap_or_default(),
+ })
+ .collect();
let mut repository_set = RepositorySet::new(
&composer.get_package().get_minimum_stability(),
composer.get_package().get_stability_flags().clone(),
- // TODO(phase-b): collect root aliases from composer.get_package().get_aliases()
- Vec::new(),
+ root_aliases,
composer.get_package().get_references().clone(),
IndexMap::new(),
IndexMap::new(),
diff --git a/crates/shirabe/src/dependency_resolver/decisions.rs b/crates/shirabe/src/dependency_resolver/decisions.rs
index 3b82883..81f0088 100644
--- a/crates/shirabe/src/dependency_resolver/decisions.rs
+++ b/crates/shirabe/src/dependency_resolver/decisions.rs
@@ -11,7 +11,6 @@ pub struct Decisions {
pub(crate) pool: std::rc::Rc<std::cell::RefCell<Pool>>,
pub(crate) decision_map: IndexMap<i64, i64>,
pub(crate) decision_queue: Vec<(i64, std::rc::Rc<std::cell::RefCell<Rule>>)>,
- iterator_cursor: Option<usize>,
}
impl std::fmt::Debug for Decisions {
@@ -32,7 +31,6 @@ impl Decisions {
pool,
decision_map: IndexMap::new(),
decision_queue: Vec::new(),
- iterator_cursor: None,
}
}
@@ -154,33 +152,6 @@ impl Decisions {
self.decision_queue.len()
}
- pub fn rewind(&mut self) {
- if self.decision_queue.is_empty() {
- self.iterator_cursor = None;
- } else {
- self.iterator_cursor = Some(self.decision_queue.len() - 1);
- }
- }
-
- pub fn current(&self) -> Option<&(i64, std::rc::Rc<std::cell::RefCell<Rule>>)> {
- self.iterator_cursor
- .and_then(|cursor| self.decision_queue.get(cursor))
- }
-
- pub fn key(&self) -> Option<usize> {
- self.iterator_cursor
- }
-
- pub fn next(&mut self) {
- self.iterator_cursor = self
- .iterator_cursor
- .and_then(|cursor| if cursor > 0 { Some(cursor - 1) } else { None });
- }
-
- pub fn valid(&self) -> bool {
- self.iterator_cursor.is_some() && self.current().is_some()
- }
-
pub fn is_empty(&self) -> bool {
self.decision_queue.is_empty()
}
@@ -227,6 +198,11 @@ impl Decisions {
str
}
+
+ // Reverse iteration: newest-first.
+ pub fn iter(&self) -> impl Iterator<Item = &(i64, std::rc::Rc<std::cell::RefCell<Rule>>)> {
+ self.decision_queue.iter().rev()
+ }
}
impl fmt::Display for Decisions {
diff --git a/crates/shirabe/src/dependency_resolver/problem.rs b/crates/shirabe/src/dependency_resolver/problem.rs
index 47cf3b5..7841946 100644
--- a/crates/shirabe/src/dependency_resolver/problem.rs
+++ b/crates/shirabe/src/dependency_resolver/problem.rs
@@ -92,7 +92,6 @@ impl Problem {
}
let reason_data = rule_ref.get_reason_data();
- // TODO(phase-b): reason_data for RULE_ROOT_REQUIRE; extract via ReasonData::RootRequire variant.
let (package_name, constraint): (String, Option<&AnyConstraint>) = match reason_data {
rule::ReasonData::RootRequire {
package_name,
@@ -144,27 +143,23 @@ impl Problem {
rule::ReasonData::RootRequire { package_name, .. } => package_name.clone(),
_ => String::new(),
},
- rule::RULE_FIXED => {
- // TODO(phase-b): reason_data for RULE_FIXED is `array{package: BasePackage}`.
- // PHP: (string) $rule->getReasonData()['package']
- match rule.get_reason_data() {
- rule::ReasonData::Fixed { package } => package.get_pretty_string(),
- _ => String::new(),
- }
- }
+ rule::RULE_FIXED => match rule.get_reason_data() {
+ rule::ReasonData::Fixed { package } => package.get_unique_name(),
+ _ => String::new(),
+ },
rule::RULE_PACKAGE_CONFLICT | rule::RULE_PACKAGE_REQUIRES => {
- // TODO(phase-b): reason_data is a Link.
let source = rule.get_source_package(pool).unwrap();
let link_pretty = match rule.get_reason_data() {
rule::ReasonData::Link(link) => link.get_pretty_string(source.clone()),
_ => String::new(),
};
- format!("{}//{}", source.get_pretty_string(), link_pretty)
+ format!("{}//{}", source.get_unique_name(), link_pretty)
}
rule::RULE_PACKAGE_SAME_NAME
| rule::RULE_PACKAGE_ALIAS
| rule::RULE_PACKAGE_INVERSE_ALIAS => {
- // TODO(phase-b): convert ReasonData to PhpMixed for php_to_string
+ // TODO(phase-c): PHP returns (string) $rule->getReasonData(), but the alias rules'
+ // reason_data is still a placeholder pending the RuleSetGenerator reason_data wiring.
format!("{:?}", rule.get_reason_data())
}
rule::RULE_LEARNED => implode(
@@ -807,7 +802,7 @@ impl Problem {
}
if pool.is_security_removed_package_version(package_name, constraint) {
- // TODO(phase-b): get_matching_security_advisories needs Vec<PackageInterfaceHandle>
+ // TODO(phase-c): get_matching_security_advisories needs Vec<PackageInterfaceHandle>
// and SecurityAdvisory.inner.advisory_id is on the private inner field.
// Convert packages to PackageInterfaceHandle and adjust SecurityAdvisory accessor first.
let _ = repository_set;
diff --git a/crates/shirabe/src/dependency_resolver/request.rs b/crates/shirabe/src/dependency_resolver/request.rs
index 88957f2..83efb9b 100644
--- a/crates/shirabe/src/dependency_resolver/request.rs
+++ b/crates/shirabe/src/dependency_resolver/request.rs
@@ -28,13 +28,15 @@ impl Request {
pub const UPDATE_LISTED_WITH_TRANSITIVE_DEPS: i64 = UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
}
-/// Represents the value of updateAllowTransitiveDependencies, which is false|UPDATE_* in PHP.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UpdateAllowTransitiveDeps {
- /// Corresponds to PHP false (initial value)
+ /// Corresponds to PHP false.
False,
+ /// \Composer\DependencyResolver\Request::UPDATE_ONLY_LISTED
UpdateOnlyListed,
+ /// \Composer\DependencyResolver\Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE
UpdateListedWithTransitiveDepsNoRootRequire,
+ /// \Composer\DependencyResolver\Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS
UpdateListedWithTransitiveDeps,
}
diff --git a/crates/shirabe/src/dependency_resolver/rule.rs b/crates/shirabe/src/dependency_resolver/rule.rs
index 7804b99..4e5a920 100644
--- a/crates/shirabe/src/dependency_resolver/rule.rs
+++ b/crates/shirabe/src/dependency_resolver/rule.rs
@@ -229,7 +229,9 @@ impl Rule {
if PlatformRepository::is_platform_package(link.get_target()) {
return false;
}
- // TODO(phase-b): Request::get_locked_repository() signature
+ // TODO(phase-c): request.get_locked_repository() exists, but its get_packages()
+ // returns Result while is_caused_by_lock returns bool; resolving needs the bool
+ // chain (also via Problem/SolverProblemsException, itself phase-c) to carry Result.
let locked_repo: Option<()> = todo!("request.get_locked_repository()");
if let Some(_locked_repo) = locked_repo {
let packages: Vec<BasePackageHandle> = todo!("locked_repo.get_packages()");
@@ -269,7 +271,9 @@ impl Rule {
if PlatformRepository::is_platform_package(package_name) {
return false;
}
- // TODO(phase-b): Request::get_locked_repository() signature
+ // TODO(phase-c): request.get_locked_repository() exists, but its get_packages()
+ // returns Result while is_caused_by_lock returns bool; resolving needs the bool
+ // chain (also via Problem/SolverProblemsException, itself phase-c) to carry Result.
let locked_repo: Option<()> = todo!("request.get_locked_repository()");
if let Some(_locked_repo) = locked_repo {
let packages: Vec<BasePackageHandle> = todo!("locked_repo.get_packages()");
diff --git a/crates/shirabe/src/dependency_resolver/solver.rs b/crates/shirabe/src/dependency_resolver/solver.rs
index 3d2113e..03dd575 100644
--- a/crates/shirabe/src/dependency_resolver/solver.rs
+++ b/crates/shirabe/src/dependency_resolver/solver.rs
@@ -239,10 +239,8 @@ impl Solver {
self.io
.write_error3("Generating rules", true, crate::io::DEBUG);
let mut rule_set_generator = RuleSetGenerator::new(self.policy.clone(), self.pool.clone());
- // TODO(phase-b): get_rules_for takes Option<Rc<dyn PlatformRequirementFilterInterface>>;
- // PHP passes the filter directly. Forwarding `None` here keeps the call typecheckable.
- let _ = platform_requirement_filter.as_ref();
- self.rules = rule_set_generator.get_rules_for(request, None)?;
+ self.rules =
+ rule_set_generator.get_rules_for(request, Some(platform_requirement_filter.clone()))?;
drop(rule_set_generator);
self.check_for_root_require_problems(request, platform_requirement_filter.as_ref())?;
self.decisions = Decisions::new(self.pool.clone());
@@ -667,22 +665,12 @@ impl Solver {
seen.insert(literal.abs(), true);
}
- // TODO(phase-b): Decisions does not expose an `iter()` matching PHP's foreach.
- // Walk the decision queue directly through offsets to avoid borrowing issues
- // (we still need to call back into `&self` while iterating).
- let mut offset = 0_usize;
- while offset < self.decisions.count() {
- let decision_literal = self.decisions.at_offset(offset).0;
-
- offset += 1;
-
+ for (decision_literal, why) in self.decisions.iter() {
// skip literals that are not in this rule
if !seen.contains_key(&decision_literal.abs()) {
continue;
}
- let why = self.decisions.at_offset(offset - 1).1.clone();
-
problem.add_rule(why.clone());
self.analyze_unsolvable_rule(&mut problem, why.clone(), &mut rule_seen);
diff --git a/crates/shirabe/src/downloader/file_downloader.rs b/crates/shirabe/src/downloader/file_downloader.rs
index 54b0882..e5112cc 100644
--- a/crates/shirabe/src/downloader/file_downloader.rs
+++ b/crates/shirabe/src/downloader/file_downloader.rs
@@ -551,12 +551,7 @@ impl DownloaderInterface for FileDownloader {
for bin in package.get_binaries() {
let bin_path = format!("{}/{}", path, bin);
if file_exists(&bin_path) && !is_executable(&bin_path) {
- // TODO(phase-b): Silencer::call_named for native PHP function
- let _ = Silencer::call(|| {
- let _ = bin_path;
- let _ = umask();
- Ok(())
- });
+ let _ = Silencer::call(|| Ok(shirabe_php_shim::chmod(&bin_path, 0o777 & !umask())));
}
}
diff --git a/crates/shirabe/src/downloader/git_downloader.rs b/crates/shirabe/src/downloader/git_downloader.rs
index 7dace1b..47dd959 100644
--- a/crates/shirabe/src/downloader/git_downloader.rs
+++ b/crates/shirabe/src/downloader/git_downloader.rs
@@ -1240,7 +1240,6 @@ impl VcsDownloader for GitDownloader {
io_interface::NORMAL,
);
let slice_end = 10_usize.min(changes.len());
- // TODO(phase-b): PHP passes the list directly to writeError; joined here so write_error3 takes &str
self.inner
.io
.write_error3(&changes[..slice_end].join("\n"), true, io_interface::NORMAL);
@@ -1291,7 +1290,6 @@ impl VcsDownloader for GitDownloader {
.into());
}
Some("v") => {
- // TODO(phase-b): PHP passes list directly; joined here for &str arg
self.inner
.io
.write_error3(&changes.join("\n"), true, io_interface::NORMAL);
@@ -1307,7 +1305,6 @@ impl VcsDownloader for GitDownloader {
if do_help {
// help:
- // TODO(phase-b): PHP passes list directly; joined here for &str arg
self.inner.io.write_error3(
&[
format!(
diff --git a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
index a9da6e8..729979d 100644
--- a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
+++ b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
@@ -1067,8 +1067,6 @@ impl EventDispatcher {
let package = composer.get_package();
let scripts = package.get_scripts();
- // TODO(phase-b): RootPackage::get_scripts() returns Vec<String> per event;
- // mirror PHP's is_empty_value semantics on the Vec form.
let event_scripts: Vec<String> = match scripts.get(event.get_name()) {
Some(v) if !v.is_empty() => v.clone(),
_ => return Vec::new(),
@@ -1271,12 +1269,10 @@ impl EventDispatcher {
package.clone(),
packages,
)?;
- // TODO(phase-b): parse_autoloads also expects the filtered dev packages list
- // (PhpMixed in this port).
let map = generator.parse_autoloads(
package_map,
package.clone(),
- shirabe_php_shim::PhpMixed::Null,
+ shirabe_php_shim::PhpMixed::Bool(false),
);
if self.loader.is_some() {
diff --git a/crates/shirabe/src/factory.rs b/crates/shirabe/src/factory.rs
index 101e4cd..f1c66ce 100644
--- a/crates/shirabe/src/factory.rs
+++ b/crates/shirabe/src/factory.rs
@@ -6,11 +6,11 @@ use shirabe_external_packages::symfony::console::formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::formatter::OutputFormatterStyle;
use shirabe_external_packages::symfony::console::output::ConsoleOutput;
use shirabe_php_shim::{
- InvalidArgumentException, PATHINFO_EXTENSION, PHP_EOL, Phar, PhpMixed, RuntimeException,
- UnexpectedValueException, ZipArchive, array_keys, array_replace_recursive, class_exists,
- dirname, extension_loaded, file_exists, file_get_contents, file_put_contents, implode,
- in_array, is_array, is_dir, is_file, is_string, json_decode, pathinfo, realpath, str_replace,
- strpos, strtr, substr, trim,
+ InvalidArgumentException, PATHINFO_EXTENSION, PHP_EOL, PHP_OS, Phar, PhpMixed,
+ RuntimeException, UnexpectedValueException, ZipArchive, array_keys, array_replace_recursive,
+ class_exists, dirname, extension_loaded, file_exists, file_get_contents, file_put_contents,
+ implode, in_array, is_array, is_dir, is_file, is_string, json_decode, mkdir, pathinfo,
+ realpath, str_replace, strpos, strtr, substr, trim,
};
use crate::autoload::AutoloadGenerator;
@@ -175,7 +175,7 @@ impl Factory {
}
let user_dir = Self::get_user_dir()?;
- if Platform::php_os() == "Darwin" {
+ if PHP_OS == "Darwin" {
// Migrate existing cache dir in old location if present
if is_dir(&format!("{}/cache", home))
&& !is_dir(&format!("{}/Library/Caches/composer", user_dir))
@@ -309,7 +309,7 @@ impl Factory {
if !is_dir(dir) {
let dir_owned = dir.clone();
let _ = Silencer::call(|| {
- Ok::<bool, anyhow::Error>(Platform::mkdir(&dir_owned, 0o777, true))
+ Ok::<bool, anyhow::Error>(mkdir(&dir_owned, 0o777, true))
});
}
let path = format!("{}/.htaccess", dir);
diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs
index 1578fc1..3c68b90 100644
--- a/crates/shirabe/src/installer.rs
+++ b/crates/shirabe/src/installer.rs
@@ -58,6 +58,7 @@ use crate::dependency_resolver::Request;
use crate::dependency_resolver::SecurityAdvisoryPoolFilter;
use crate::dependency_resolver::Solver;
use crate::dependency_resolver::SolverProblemsException;
+use crate::dependency_resolver::UpdateAllowTransitiveDeps;
use crate::dependency_resolver::operation::InstallOperation;
use crate::dependency_resolver::operation::OperationInterface;
use crate::dependency_resolver::operation::UninstallOperation;
@@ -143,7 +144,7 @@ pub struct Installer {
allowed_types: Option<Vec<String>>,
pub(crate) update_mirrors: bool,
pub(crate) update_allow_list: Option<Vec<String>>,
- pub(crate) update_allow_transitive_dependencies: i64,
+ pub(crate) update_allow_transitive_dependencies: UpdateAllowTransitiveDeps,
pub(crate) suggested_packages_reporter:
std::rc::Rc<std::cell::RefCell<SuggestedPackagesReporter>>,
pub(crate) platform_requirement_filter: std::rc::Rc<dyn PlatformRequirementFilterInterface>,
@@ -217,7 +218,7 @@ impl Installer {
allowed_types: None,
update_mirrors: false,
update_allow_list: None,
- update_allow_transitive_dependencies: Request::UPDATE_ONLY_LISTED,
+ update_allow_transitive_dependencies: UpdateAllowTransitiveDeps::UpdateOnlyListed,
suggested_packages_reporter,
platform_requirement_filter,
additional_fixed_repository: None,
@@ -653,8 +654,10 @@ impl Installer {
// pass the allow list into the request, so the pool builder can apply it
if let Some(ref allow_list) = self.update_allow_list {
- // TODO(phase-b): convert i64 self.update_allow_transitive_dependencies into the enum
- let _ = allow_list;
+ request.set_update_allow_list(
+ allow_list.clone(),
+ self.update_allow_transitive_dependencies,
+ );
}
let pool = std::rc::Rc::new(std::cell::RefCell::new(repository_set.create_pool(
@@ -1324,7 +1327,7 @@ impl Installer {
.borrow_mut()
.get_minimum_stability()
.unwrap_or_else(|_| String::new());
- // TODO(phase-b): locker.get_stability_flags returns IndexMap<String, String>; convert to i64
+ // locker stores stability flags as stringified ints; recover the int form here.
stability_flags = self
.locker
.borrow_mut()
@@ -1378,9 +1381,15 @@ impl Installer {
[VersionParser::parse_stability(&self.package.get_version()).as_str()],
);
- // TODO(phase-b): convert root_aliases (Vec<IndexMap<String, String>>) into Vec<RootAliasInput>
- let root_aliases_input: Vec<crate::repository::RootAliasInput> = vec![];
- let _ = root_aliases;
+ let root_aliases_input: Vec<crate::repository::RootAliasInput> = root_aliases
+ .into_iter()
+ .map(|alias| crate::repository::RootAliasInput {
+ package: alias.get("package").cloned().unwrap_or_default(),
+ version: alias.get("version").cloned().unwrap_or_default(),
+ alias: alias.get("alias").cloned().unwrap_or_default(),
+ alias_normalized: alias.get("alias_normalized").cloned().unwrap_or_default(),
+ })
+ .collect();
let temporary_constraints: IndexMap<String, AnyConstraint> = IndexMap::new();
let mut repository_set = RepositorySet::new(
&minimum_stability,
@@ -1522,20 +1531,11 @@ impl Installer {
) -> anyhow::Result<()> {
// if we're updating mirrors we want to keep exactly the same versions installed which are in the lock file, but we want current remote metadata
if self.update_mirrors {
- let excluded_packages: IndexMap<String, i64> = if !include_dev_requires {
- // TODO(phase-b): locker.get_dev_package_names returns Result<Vec<String>>
- let names = self
- .locker
- .borrow_mut()
- .get_dev_package_names()
- .unwrap_or_default();
- names
- .into_iter()
- .enumerate()
- .map(|(i, name)| (name, i as i64))
- .collect()
+ let excluded_packages: indexmap::IndexSet<String> = if !include_dev_requires {
+ let names = self.locker.borrow_mut().get_dev_package_names()?;
+ names.into_iter().collect()
} else {
- IndexMap::new()
+ indexmap::IndexSet::new()
};
for locked_package in locked_repository
@@ -1546,7 +1546,7 @@ impl Installer {
// exclude alias packages here as for root aliases, both alias and aliased are
// present in the lock repo and we only want to require the aliased version
if locked_package.as_alias().is_none()
- && !excluded_packages.contains_key(&locked_package.get_name())
+ && !excluded_packages.contains(&locked_package.get_name())
{
request.require_name(
&locked_package.get_name(),
@@ -1893,21 +1893,8 @@ impl Installer {
/// dependencies which are not root requirement or all transitive dependencies including root requirements
pub fn set_update_allow_transitive_dependencies(
&mut self,
- update_allow_transitive_dependencies: i64,
+ update_allow_transitive_dependencies: UpdateAllowTransitiveDeps,
) -> anyhow::Result<&mut Self> {
- let valid = [
- Request::UPDATE_ONLY_LISTED,
- Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE,
- Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS,
- ];
- if !valid.contains(&update_allow_transitive_dependencies) {
- return Err(RuntimeException {
- message: "Invalid value for updateAllowTransitiveDependencies supplied".to_string(),
- code: 0,
- }
- .into());
- }
-
self.update_allow_transitive_dependencies = update_allow_transitive_dependencies;
Ok(self)
diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs
index 4a13e6b..4d5608d 100644
--- a/crates/shirabe/src/installer/installation_manager.rs
+++ b/crates/shirabe/src/installer/installation_manager.rs
@@ -486,7 +486,7 @@ impl InstallationManager {
};
if run_scripts && self.event_dispatcher.is_some() {
- // TODO(phase-b): dispatch_package_event takes Box<dyn RepositoryInterface>/Vec<Box<...>>
+ // TODO(phase-c): dispatch_package_event takes Box<dyn RepositoryInterface>/Vec<Box<...>>
// but we hold &mut dyn here. Needs structural rework (likely shared Rc on repo and ops).
let _ = (
event_name,
diff --git a/crates/shirabe/src/io/buffer_io.rs b/crates/shirabe/src/io/buffer_io.rs
index 079b470..92f5714 100644
--- a/crates/shirabe/src/io/buffer_io.rs
+++ b/crates/shirabe/src/io/buffer_io.rs
@@ -59,8 +59,8 @@ impl BufferIO {
}
pub fn get_output(&self) -> String {
- // TODO(phase-b): OutputInterface::get_stream returns PhpResource, while
- // fseek/stream_get_contents take PhpMixed. Conversion is not yet defined.
+ // TODO(phase-c): OutputInterface::get_stream returns PhpResource, while
+ // fseek/stream_get_contents take PhpMixed. The PhpResource stream model is not yet defined.
let stream: PhpMixed =
todo!("PhpResource -> PhpMixed conversion for OutputInterface::get_stream");
fseek(stream.clone(), 0);
@@ -93,7 +93,8 @@ impl BufferIO {
&output,
);
- // TODO(phase-b): Preg::replace_callback returns Result<String>, unwrap for now
+ // TODO(phase-c): Preg::replace_callback returns Result<String>; PHP getOutput returns the
+ // string directly, so this is gated on the get_stream PhpResource model above.
output.unwrap_or_default()
}
diff --git a/crates/shirabe/src/package/version/version_guesser.rs b/crates/shirabe/src/package/version/version_guesser.rs
index 1afe910..3b61eac 100644
--- a/crates/shirabe/src/package/version/version_guesser.rs
+++ b/crates/shirabe/src/package/version/version_guesser.rs
@@ -436,8 +436,6 @@ impl VersionGuesser {
],
path,
)?;
- // PHP: $result['commit'] = '';
- // TODO(phase-b): VersionData::commit modeled as Option<String>; using Some(String::new())
let commit = Some(String::new());
let feature_version = Some(version.clone());
let feature_pretty_version = Some(version);
diff --git a/crates/shirabe/src/repository/array_repository.rs b/crates/shirabe/src/repository/array_repository.rs
index df25235..1bee17b 100644
--- a/crates/shirabe/src/repository/array_repository.rs
+++ b/crates/shirabe/src/repository/array_repository.rs
@@ -144,6 +144,11 @@ impl ArrayRepository {
pub(crate) fn initialize(&self) {
*self.packages.borrow_mut() = Some(vec![]);
}
+
+ /// Resets the packages cache so the next access re-runs `initialize`.
+ pub(crate) fn reset_packages(&self) {
+ *self.packages.borrow_mut() = None;
+ }
}
impl RepositoryInterface for ArrayRepository {
diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs
index 4a18dc9..411fa13 100644
--- a/crates/shirabe/src/repository/filesystem_repository.rs
+++ b/crates/shirabe/src/repository/filesystem_repository.rs
@@ -186,8 +186,7 @@ impl FilesystemRepository {
}
pub fn reload(&mut self) -> Result<()> {
- // TODO(phase-b): clear inner packages cache (PHP: $this->packages = null)
- self.inner.reload();
+ self.inner.reset_packages();
self.initialize()
}
diff --git a/crates/shirabe/src/repository/repository_factory.rs b/crates/shirabe/src/repository/repository_factory.rs
index 89334a1..993699f 100644
--- a/crates/shirabe/src/repository/repository_factory.rs
+++ b/crates/shirabe/src/repository/repository_factory.rs
@@ -321,10 +321,10 @@ impl RepositoryFactory {
Ok(repo_map)
}
- pub fn generate_repository_name(
+ pub fn generate_repository_name<T>(
index: &PhpMixed,
repo: &IndexMap<String, PhpMixed>,
- existing_repos: &IndexMap<String, RepositoryInterfaceHandle>,
+ existing_repos: &IndexMap<String, T>,
) -> String {
let mut name = match index {
PhpMixed::Int(_) => {
diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs
index f632fbc..bf7b525 100644
--- a/crates/shirabe/src/repository/repository_set.rs
+++ b/crates/shirabe/src/repository/repository_set.rs
@@ -618,7 +618,6 @@ impl RepositorySet {
}
if !allowed_packages.is_empty() {
- // TODO(phase-b): Request::restrict_packages signature
request.restrict_packages(allowed_packages);
}
diff --git a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
index 7eb253a..59e6b88 100644
--- a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
+++ b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
@@ -494,7 +494,9 @@ impl GitBitbucketDriver {
/// @inheritDoc
pub fn get_source(&self, identifier: &str) -> IndexMap<String, String> {
if let Some(fallback) = self.fallback_driver.as_ref() {
- // TODO(phase-b): trait returns Result; flatten for the inherent signature here
+ // TODO(phase-c): PHP getSource is infallible (: array), but the Rust trait made it
+ // Result, so the fallback's Result is flattened here. The faithful fix is making the
+ // VcsDriverInterface get_source/get_dist infallible across all implementations.
return fallback.get_source(identifier).unwrap_or_default();
}
@@ -511,7 +513,7 @@ impl GitBitbucketDriver {
/// @inheritDoc
pub fn get_dist(&self, identifier: &str) -> Option<IndexMap<String, String>> {
if let Some(fallback) = self.fallback_driver.as_ref() {
- // TODO(phase-b): trait returns Result; flatten for the inherent signature here
+ // TODO(phase-c): see get_source above — the trait's over-fallibility is flattened here.
return fallback.get_dist(identifier).ok().flatten();
}
@@ -886,14 +888,14 @@ impl GitBitbucketDriver {
}
if !extension_loaded("openssl") {
- io.write_error(
+ io.write_error3(
&format!(
"Skipping Bitbucket git driver for {} because the OpenSSL PHP extension is missing.",
url
),
+ true,
+ io_interface::VERBOSE,
);
- // PHP: writeError(..., true, io_interface::VERBOSE)
- // TODO(phase-b): io_interface::VERBOSE verbosity argument
return Ok(false);
}
diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs
index f4e2bba..911e982 100644
--- a/crates/shirabe/src/repository/vcs/svn_driver.rs
+++ b/crates/shirabe/src/repository/vcs/svn_driver.rs
@@ -33,8 +33,6 @@ pub struct SvnDriver {
/// @var ?string
pub(crate) root_identifier: Option<String>,
- /// @var string|false
- // TODO(phase-b): PHP uses 'false' as a sentinel; model as Option<String>
pub(crate) trunk_path: Option<String>,
/// @var string
pub(crate) branches_path: String,
@@ -79,8 +77,10 @@ impl SvnDriver {
SvnUtil::clean_env();
- if let Some(PhpMixed::String(v)) = self.inner.repo_config.get("trunk-path").cloned() {
- self.trunk_path = Some(v);
+ match self.inner.repo_config.get("trunk-path") {
+ Some(PhpMixed::Bool(false)) => self.trunk_path = None,
+ Some(PhpMixed::String(v)) => self.trunk_path = Some(v.clone()),
+ _ => {}
}
if let Some(PhpMixed::String(v)) = self.inner.repo_config.get("branches-path").cloned() {
self.branches_path = v;
@@ -100,10 +100,11 @@ impl SvnDriver {
self.package_path = format!("/{}", trim(&v, Some("/")));
}
- if let Some(trunk_path) = &self.trunk_path {
- if let Some(pos) = strrpos(&self.inner.url, &format!("/{}", trunk_path)) {
- self.base_url = substr(&self.inner.url, 0, Some(pos as i64));
- }
+ if let Some(pos) = strrpos(
+ &self.inner.url,
+ &format!("/{}", self.trunk_path.as_deref().unwrap_or("")),
+ ) {
+ self.base_url = substr(&self.inner.url, 0, Some(pos as i64));
}
self.inner.cache = Some(Cache::new(
diff --git a/crates/shirabe/src/repository/vcs/vcs_driver.rs b/crates/shirabe/src/repository/vcs/vcs_driver.rs
index 1c05933..740d00e 100644
--- a/crates/shirabe/src/repository/vcs/vcs_driver.rs
+++ b/crates/shirabe/src/repository/vcs/vcs_driver.rs
@@ -234,7 +234,6 @@ pub trait VcsDriver: VcsDriverInterface {
if self.should_cache(identifier) {
if let Some(ref composer_map) = composer {
- // TODO(phase-b): use a dedicated encode-with-options helper; reuse encode for now.
let composer_mixed = PhpMixed::Array(
composer_map
.iter()
diff --git a/crates/shirabe/src/repository/writable_array_repository.rs b/crates/shirabe/src/repository/writable_array_repository.rs
index bf9d094..0615ef6 100644
--- a/crates/shirabe/src/repository/writable_array_repository.rs
+++ b/crates/shirabe/src/repository/writable_array_repository.rs
@@ -48,6 +48,10 @@ impl WritableArrayRepository {
self.dev_mode = None;
}
+ pub fn reset_packages(&self) {
+ self.inner.reset_packages();
+ }
+
pub fn add_package(&mut self, package: crate::package::PackageInterfaceHandle) -> Result<()> {
self.inner.add_package(package)
}
diff --git a/crates/shirabe/src/util/http_downloader.rs b/crates/shirabe/src/util/http_downloader.rs
index 5d0eae6..434dedb 100644
--- a/crates/shirabe/src/util/http_downloader.rs
+++ b/crates/shirabe/src/util/http_downloader.rs
@@ -779,7 +779,8 @@ impl HttpDownloader {
);
http_map.insert("ignore_errors".to_string(), Box::new(PhpMixed::Bool(true)));
ctx_options.insert("http".to_string(), PhpMixed::Array(http_map));
- // TODO(phase-b): file_get_contents only takes a path; stream context arg dropped.
+ // TODO(phase-c): file_get_contents only takes a path; the stream context arg is dropped
+ // until the PHP stream-context layer is modeled.
let _ = stream_context_create(&ctx_options, None);
let test_connectivity = file_get_contents("https://8.8.8.8");
Silencer::restore();
diff --git a/crates/shirabe/src/util/platform.rs b/crates/shirabe/src/util/platform.rs
index 3866a12..6a0c2d0 100644
--- a/crates/shirabe/src/util/platform.rs
+++ b/crates/shirabe/src/util/platform.rs
@@ -186,7 +186,6 @@ impl Platform {
return false;
}
- // TODO(phase-b): Silencer::call returns Result; PHP returns the value or false on error
let file_contents = Silencer::call(|| Ok(file_get_contents("/proc/version")))
.ok()
.flatten()
@@ -294,7 +293,8 @@ impl Platform {
Some(f) => f,
None => {
if defined("STDOUT") {
- // TODO(phase-b): map STDOUT to the runtime stdout resource
+ // TODO(phase-c): map the STDOUT constant to a runtime stdout resource; depends
+ // on the unmodeled PHP stream/resource layer.
todo!("STDOUT constant")
} else {
let fd = fopen("php://stdout", "w");
@@ -330,7 +330,6 @@ impl Platform {
return true;
}
- // TODO(phase-b): Silencer::call wraps the fstat call (`@fstat($fd)`)
let stat = Silencer::call(|| Ok(fstat(fd)));
let stat = match stat {
Ok(s) => s,
@@ -427,25 +426,8 @@ impl Platform {
"/dev/null".to_string()
}
- /// PHP: PHP_OS — returns the OS PHP was built on.
- pub fn php_os() -> &'static str {
- // TODO(phase-b): map to actual OS name (e.g. "Darwin", "Linux", "WINNT").
- todo!()
- }
-
/// PHP: rename($from, $to) — wrap the std rename so callers can use Platform::rename.
pub fn rename(from: &str, to: &str) -> bool {
std::fs::rename(from, to).is_ok()
}
-
- /// PHP: mkdir($pathname, $mode, $recursive)
- pub fn mkdir(pathname: &str, _mode: u32, recursive: bool) -> bool {
- // TODO(phase-b): honor mode bits on Unix
- let result = if recursive {
- std::fs::create_dir_all(pathname)
- } else {
- std::fs::create_dir(pathname)
- };
- result.is_ok()
- }
}
diff --git a/crates/shirabe/src/util/remote_filesystem.rs b/crates/shirabe/src/util/remote_filesystem.rs
index bd4aaa8..dc6a2a4 100644
--- a/crates/shirabe/src/util/remote_filesystem.rs
+++ b/crates/shirabe/src/util/remote_filesystem.rs
@@ -720,7 +720,8 @@ impl RemoteFilesystem {
}
let mut caught_e: Option<anyhow::Error> = None;
- // TODO(phase-b): wrap PHP's `file_get_contents` with stream context and error capture.
+ // TODO(phase-c): wrap PHP's `file_get_contents` with stream context and error capture;
+ // depends on the unmodeled PHP stream-context layer.
let outer: Result<Option<String>, anyhow::Error> = Ok(None);
match outer {
Ok(v) => result = v,
@@ -743,7 +744,8 @@ impl RemoteFilesystem {
*response_headers = http_get_last_response_headers().unwrap_or_default();
http_clear_last_response_headers();
} else {
- // TODO(phase-b): read the magic `$http_response_header` PHP variable.
+ // TODO(phase-c): read the magic `$http_response_header` PHP variable; depends on the
+ // unmodeled PHP stream layer that populates it.
*response_headers = Vec::new();
}