diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-10 02:41:34 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-10 02:46:18 +0900 |
| commit | 2d474e91e49c7343d28198eff2b5bbbed9afbcee (patch) | |
| tree | 8c1ab321dfa5ddc1ca9d2871eb06a6fde6b2970b /crates/shirabe | |
| parent | e583112899cbea7494ffdd73d7de380dd5f808c4 (diff) | |
| download | php-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/shirabe')
35 files changed, 171 insertions, 237 deletions
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(); } |
