diff options
Diffstat (limited to 'crates')
13 files changed, 91 insertions, 71 deletions
diff --git a/crates/shirabe-external-packages/src/symfony/console/formatter/output_formatter_style_stack.rs b/crates/shirabe-external-packages/src/symfony/console/formatter/output_formatter_style_stack.rs index 5682b644..2464c028 100644 --- a/crates/shirabe-external-packages/src/symfony/console/formatter/output_formatter_style_stack.rs +++ b/crates/shirabe-external-packages/src/symfony/console/formatter/output_formatter_style_stack.rs @@ -36,10 +36,7 @@ impl OutputFormatterStyleStack { ) -> anyhow::Result<Result<Box<dyn OutputFormatterStyleInterface>, InvalidArgumentException>> { if self.styles.is_empty() { - // Returns the shared empty style; ownership cannot be expressed without Clone on the - // trait object. - // TODO(human-review): empty-style branch needs a Clone/Rc strategy in Phase C. - return Ok(Ok(todo!())); + return Ok(Ok(self.empty_style.clone_box())); } let style = match style.as_mut() { diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs index cd2d1648..25d32b2b 100644 --- a/crates/shirabe/src/command/archive_command.rs +++ b/crates/shirabe/src/command/archive_command.rs @@ -12,6 +12,7 @@ use crate::io::IOInterfaceImmutable; use crate::package::archiver::ArchiveManagerInterface; use crate::package::version::VersionParser; use crate::package::version::VersionSelector; +use crate::package::version::version_selector::ShowWarnings; use crate::plugin::CommandEvent; use crate::plugin::PluginEvents; use crate::repository::CompositeRepository; @@ -424,7 +425,7 @@ impl ArchiveCommand { None, 0, None, - shirabe_php_shim::PhpMixed::Bool(true), + ShowWarnings::Always, )?; let p = best.unwrap_or_else(|| packages.into_iter().next().unwrap()); diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs index 9a2c4755..8ea4f07f 100644 --- a/crates/shirabe/src/command/create_project_command.rs +++ b/crates/shirabe/src/command/create_project_command.rs @@ -21,6 +21,7 @@ use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::version::VersionParser; use crate::package::version::VersionSelector; +use crate::package::version::version_selector::ShowWarnings; use crate::package::{STABILITIES, SUPPORTED_LINK_TYPES}; use crate::plugin::PluginBlockedException; use crate::repository::CompositeRepository; @@ -895,7 +896,7 @@ impl CreateProjectCommand { Some(platform_requirement_filter.clone()), 0, Some(io.clone()), - PhpMixed::Bool(true), + ShowWarnings::Always, )?; if package.is_none() { @@ -921,7 +922,7 @@ impl CreateProjectCommand { Some(PlatformRequirementFilterFactory::ignore_all()), 0, None, - PhpMixed::Bool(true), + ShowWarnings::Always, )? .is_some() { diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs index f8f7cede..830f1996 100644 --- a/crates/shirabe/src/command/package_discovery_trait.rs +++ b/crates/shirabe/src/command/package_discovery_trait.rs @@ -10,6 +10,7 @@ use crate::io::io_interface; use crate::package::PackageInterfaceHandle; use crate::package::version::VersionParser; use crate::package::version::VersionSelector; +use crate::package::version::version_selector::ShowWarnings; use crate::repository::CompositeRepository; use crate::repository::PlatformRepository; use crate::repository::PlatformRepositoryHandle; @@ -486,7 +487,7 @@ pub trait PackageDiscoveryTrait: BaseCommand { Some(platform_requirement_filter.clone()), 0, None, - shirabe_php_shim::PhpMixed::Null, + ShowWarnings::Always, )?; if package.is_none() { @@ -544,7 +545,7 @@ pub trait PackageDiscoveryTrait: BaseCommand { Some(PlatformRequirementFilterFactory::ignore_all()), 0, None, - shirabe_php_shim::PhpMixed::Null, + ShowWarnings::Always, )?; if let Some(candidate) = candidate { return Err(InvalidArgumentException { @@ -569,7 +570,7 @@ pub trait PackageDiscoveryTrait: BaseCommand { Some(platform_requirement_filter.clone()), RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES, None, - shirabe_php_shim::PhpMixed::Null, + ShowWarnings::Always, )?; if let Some(package) = package_at_unacceptable { // we must first verify if a valid package would be found in a lower priority repository @@ -580,7 +581,7 @@ pub trait PackageDiscoveryTrait: BaseCommand { Some(PlatformRequirementFilterFactory::ignore_nothing()), RepositorySet::ALLOW_SHADOWED_REPOSITORIES, None, - shirabe_php_shim::PhpMixed::Null, + ShowWarnings::Always, )?; if let Some(all_repos_package) = all_repos_package { return Err(InvalidArgumentException { @@ -620,7 +621,7 @@ pub trait PackageDiscoveryTrait: BaseCommand { Some(PlatformRequirementFilterFactory::ignore_all()), RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES, None, - shirabe_php_shim::PhpMixed::Null, + ShowWarnings::Always, )?; if let Some(candidate) = candidate { let mut additional = String::new(); @@ -631,7 +632,7 @@ pub trait PackageDiscoveryTrait: BaseCommand { Some(PlatformRequirementFilterFactory::ignore_all()), 0, None, - shirabe_php_shim::PhpMixed::Null, + ShowWarnings::Always, )?; if no_match.is_none() { additional = format!( diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs index 8d1f67ff..4ba20575 100644 --- a/crates/shirabe/src/command/show_command.rs +++ b/crates/shirabe/src/command/show_command.rs @@ -16,6 +16,7 @@ use crate::package::PackageInterfaceHandle; use crate::package::base_package; use crate::package::version::VersionParser; use crate::package::version::VersionSelector; +use crate::package::version::version_selector::ShowWarnings; use crate::plugin::CommandEvent; use crate::plugin::PluginEvents; use crate::repository::ComposerRepository; @@ -540,18 +541,14 @@ impl Command for ShowCommand { .iter() .map(crate::package::PackageInterfaceHandle::dup) .collect(); - installed_repo = RepositoryInterfaceHandle::new(InstalledRepository::new(vec![ - root_repo.clone(), - RepositoryInterfaceHandle::new(InstalledArrayRepository::new_with_packages( - cloned, - )?), - ])); - repos = RepositoryInterfaceHandle::new(InstalledRepository::new(vec![ + let new_repo = RepositoryInterfaceHandle::new(InstalledRepository::new(vec![ root_repo, RepositoryInterfaceHandle::new(InstalledArrayRepository::new_with_packages( - Vec::new(), + cloned, )?), ])); + installed_repo = new_repo.clone(); + repos = new_repo; } else { let repository_manager = composer_local.get_repository_manager().clone(); let repository_manager = repository_manager.borrow(); @@ -619,7 +616,7 @@ impl Command for ShowCommand { && !pf.contains('*') { let (matched_package, vers) = self.get_package( - &*installed_repo.borrow(), + &installed_repo, &repos, pf, input.borrow().get_argument("version")?, @@ -686,11 +683,8 @@ impl Command for ShowCommand { let mut exit_code: i64 = 0; if input.borrow().get_option("tree")?.as_bool() == Some(true) { - let array_tree = self.generate_package_tree( - package.clone().into(), - &*installed_repo.borrow(), - &repos, - ); + let array_tree = + self.generate_package_tree(package.clone().into(), &installed_repo, &repos); if format == "json" { let mut wrapper: IndexMap<String, PhpMixed> = IndexMap::new(); @@ -812,7 +806,7 @@ impl Command for ShowCommand { ) { array_tree.push(self.generate_package_tree( package.clone(), - &*installed_repo.borrow(), + &installed_repo, &repos, )); } @@ -1656,7 +1650,7 @@ impl ShowCommand { /// finds a package by name and version if provided pub(crate) fn get_package( &self, - installed_repo: &dyn RepositoryInterface, + installed_repo: &RepositoryInterfaceHandle, repos: &RepositoryInterfaceHandle, name: &str, version: PhpMixed, @@ -2462,7 +2456,7 @@ impl ShowCommand { pub(crate) fn generate_package_tree( &self, package: PackageInterfaceHandle, - installed_repo: &dyn RepositoryInterface, + installed_repo: &RepositoryInterfaceHandle, remote_repos: &RepositoryInterfaceHandle, ) -> IndexMap<String, PhpMixed> { let requires = { @@ -2613,7 +2607,7 @@ impl ShowCommand { &self, name: &str, link: &Link, - installed_repo: &dyn RepositoryInterface, + installed_repo: &RepositoryInterfaceHandle, remote_repos: &RepositoryInterfaceHandle, packages_in_tree: &[PhpMixed], ) -> anyhow::Result<Vec<IndexMap<String, PhpMixed>>> { @@ -2817,12 +2811,12 @@ impl ShowCommand { } } - let show_warnings_box: Box<dyn Fn(PackageInterfaceHandle) -> bool> = - if self.get_io().is_verbose() { - Box::new(|_p: PackageInterfaceHandle| -> bool { true }) - } else { - let package_version = package.get_version(); - Box::new(move |candidate: PackageInterfaceHandle| -> bool { + let show_warnings = if self.get_io().is_verbose() { + ShowWarnings::Always + } else { + let package_version = package.get_version(); + ShowWarnings::Predicate(Box::new( + move |candidate: &PackageInterfaceHandle| -> bool { if candidate.get_version().starts_with("dev-") || package_version.starts_with("dev-") { @@ -2830,11 +2824,9 @@ impl ShowCommand { } version_compare(&candidate.get_version(), &package_version, "<=") - }) - }; - // 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(), @@ -2842,7 +2834,7 @@ impl ShowCommand { Some(platform_req_filter), 0, Some(self.get_io().clone()), - PhpMixed::Bool(true), + show_warnings, )?; while let Some(ref c) = candidate { if let Some(alias) = c.as_alias() { diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs index 0d212350..b47c51d0 100644 --- a/crates/shirabe/src/command/update_command.rs +++ b/crates/shirabe/src/command/update_command.rs @@ -16,6 +16,7 @@ use crate::package::base_package; use crate::package::loader::RootPackageLoader; use crate::package::version::VersionParser; use crate::package::version::VersionSelector; +use crate::package::version::version_selector::ShowWarnings; use crate::plugin::CommandEvent; use crate::plugin::PluginEvents; use crate::repository::CanonicalPackagesTrait; @@ -669,7 +670,7 @@ impl UpdateCommand { None, 0, None, - PhpMixed::Bool(true), + ShowWarnings::Always, )?; let _ = &platform_req_filter; if let Some(latest) = latest_version diff --git a/crates/shirabe/src/downloader/file_downloader.rs b/crates/shirabe/src/downloader/file_downloader.rs index cbf008c4..aaa3995a 100644 --- a/crates/shirabe/src/downloader/file_downloader.rs +++ b/crates/shirabe/src/downloader/file_downloader.rs @@ -126,10 +126,11 @@ impl FileDownloader { if let Some(cache) = &this.cache && cache.borrow().gc_is_necessary() { - // PHP: writeError('Running cache garbage collection', true, io_interface::VERY_VERBOSE) - this.io - .borrow() - .write_error("Running cache garbage collection"); + this.io.borrow().write_error3( + "Running cache garbage collection", + true, + crate::io::io_interface::VERY_VERBOSE, + ); let ttl = this .config .borrow_mut() diff --git a/crates/shirabe/src/package/complete_package.rs b/crates/shirabe/src/package/complete_package.rs index 0c7ecf49..ec9986c9 100644 --- a/crates/shirabe/src/package/complete_package.rs +++ b/crates/shirabe/src/package/complete_package.rs @@ -47,6 +47,10 @@ impl CompletePackage { pub fn replace_version(&mut self, version: String, pretty_version: String) { self.inner.replace_version(version, pretty_version); } + + pub fn set_release_date(&mut self, release_date: Option<chrono::DateTime<chrono::Utc>>) { + self.inner.set_release_date(release_date); + } } impl CompletePackageInterface for CompletePackage { diff --git a/crates/shirabe/src/package/handle.rs b/crates/shirabe/src/package/handle.rs index 2b23a8c2..4d78734f 100644 --- a/crates/shirabe/src/package/handle.rs +++ b/crates/shirabe/src/package/handle.rs @@ -1570,6 +1570,13 @@ impl CompletePackageHandle { pub fn new(name: String, version: String, pretty_version: String) -> Self { Self::from_complete_package(CompletePackage::new(name, version, pretty_version)) } + + pub fn set_release_date(&self, release_date: Option<chrono::DateTime<chrono::Utc>>) { + match &mut *self.0.borrow_mut() { + AnyPackage::CompletePackage(p) => p.set_release_date(release_date), + _ => unreachable!("CompletePackageHandle invariant"), + } + } } impl RootPackageHandle { @@ -1589,6 +1596,13 @@ impl RootPackageHandle { _ => unreachable!("RootPackageHandle invariant"), } } + + pub fn set_release_date(&self, release_date: Option<chrono::DateTime<chrono::Utc>>) { + match &mut *self.0.borrow_mut() { + AnyPackage::RootPackage(p) => p.set_release_date(release_date), + _ => unreachable!("RootPackageHandle invariant"), + } + } } impl AliasPackageHandle { diff --git a/crates/shirabe/src/package/root_package.rs b/crates/shirabe/src/package/root_package.rs index 899006d1..2c2b87ea 100644 --- a/crates/shirabe/src/package/root_package.rs +++ b/crates/shirabe/src/package/root_package.rs @@ -42,6 +42,10 @@ impl RootPackage { pub fn replace_version(&mut self, version: String, pretty_version: String) { self.inner.replace_version(version, pretty_version); } + + pub fn set_release_date(&mut self, release_date: Option<DateTime<Utc>>) { + self.inner.set_release_date(release_date); + } } impl RootPackageInterface for RootPackage { diff --git a/crates/shirabe/src/package/version/version_selector.rs b/crates/shirabe/src/package/version/version_selector.rs index ff5e7586..e8539212 100644 --- a/crates/shirabe/src/package/version/version_selector.rs +++ b/crates/shirabe/src/package/version/version_selector.rs @@ -24,6 +24,13 @@ use shirabe_php_shim::{ use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::SimpleConstraint; +/// PHP `$showWarnings` (`true` or a `callable(PackageInterface): bool`) as passed to +/// `VersionSelector::findBestCandidate`. +pub enum ShowWarnings { + Always, + Predicate(Box<dyn Fn(&PackageInterfaceHandle) -> bool>), +} + #[derive(Debug)] pub struct VersionSelector { repository_set: std::rc::Rc<std::cell::RefCell<dyn RepositorySetInterface>>, @@ -66,7 +73,7 @@ impl VersionSelector { platform_requirement_filter: Option<std::rc::Rc<dyn PlatformRequirementFilterInterface>>, repo_set_flags: i64, io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>, - show_warnings: shirabe_php_shim::PhpMixed, + show_warnings: ShowWarnings, ) -> anyhow::Result<Option<crate::package::PackageInterfaceHandle>> { if !base_package::STABILITIES.contains_key(preferred_stability) { return Err(shirabe_php_shim::UnexpectedValueException { @@ -179,8 +186,8 @@ impl VersionSelector { already_seen_names.insert(pkg.get_name().to_string(), true); if let Some(ref io) = io { let should_warn = match &show_warnings { - shirabe_php_shim::PhpMixed::Bool(b) => *b, - _ => true, + ShowWarnings::Always => true, + ShowWarnings::Predicate(f) => f(pkg), }; if should_warn { let warn_key = format!("{}/{}", pkg.get_name(), link.get_target()); diff --git a/crates/shirabe/tests/command/show_command_test.rs b/crates/shirabe/tests/command/show_command_test.rs index 46ec9c3d..380371c4 100644 --- a/crates/shirabe/tests/command/show_command_test.rs +++ b/crates/shirabe/tests/command/show_command_test.rs @@ -50,18 +50,17 @@ fn show_composer_json(requires: serde_json::Value) -> serde_json::Value { } /// ref: ShowCommandTest::testShow (one data-provider case). -/// -/// NOTE: PHP sets release dates on the installed packages via `setReleaseDate`, but no such setter -/// is exposed on `PackageInterfaceHandle`, so it is omitted here. Only the "sorting by age" case -/// depends on release dates; every other case produces identical output without them. fn run_show_case(command: Vec<(PhpMixed, PhpMixed)>, expected: &str, requires: serde_json::Value) { let _tear_down = init_temp_composer(Some(&show_composer_json(requires)), None, None, true); let pkg = get_complete_package("vendor/package", "v1.0.0"); pkg.set_description("description of installed package".to_string()); let major = get_complete_package("outdated/major", "v1.0.0"); + major.set_release_date(Some(chrono::Utc::now())); let minor = get_complete_package("outdated/minor", "1.0.0"); + minor.set_release_date(Some(chrono::Utc::now() - chrono::Duration::days(365 * 2))); let patch = get_complete_package("outdated/patch", "1.0.0"); + patch.set_release_date(Some(chrono::Utc::now() - chrono::Duration::weeks(2))); let packages: Vec<PackageInterfaceHandle> = vec![pkg.into(), major.into(), minor.into(), patch.into()]; @@ -187,7 +186,6 @@ outdated/patch 1.0.0 <highlight>! 1.0.1</highlight>", ); } -#[ignore = "blocked on two counts: (1) same package-categorization gap as the other outdated tests (packages land in the \"available\" bucket so the \"Direct/Transitive dependencies\" grouping is missing); (2) PackageInterfaceHandle exposes no set_release_date setter, so the sorting-by-age output (\"2 years old\", \"2 weeks old\", \"from today\") cannot be reproduced. Neither is an output-format issue."] #[test] #[serial] fn test_show_outdated_deps_sorting_by_age() { @@ -314,7 +312,6 @@ outdated/patch 1.0.0 <highlight>! 1.0.1</highlight>", ); } -#[ignore = "blocked: categorization fixed, but the --latest path still resolves the wrong latest-package version (gets 1.0.0, expects 1.3.0); outdated version-resolution gap, not categorization"] #[test] #[serial] fn test_outdated_filters_according_to_platform_reqs_and_warns() { @@ -387,7 +384,6 @@ vendor/package 1.1.0 ~ 1.0.0", ); } -#[ignore = "blocked: categorization fixed, but the --latest path resolves the wrong latest version (gets 1.2.0, expects 1.3.0); outdated version-resolution gap, not categorization"] #[test] #[serial] fn test_outdated_filters_according_to_platform_reqs_without_warning_for_higher_versions() { @@ -544,12 +540,21 @@ fn test_show_direct_with_name_only_shows_direct_dependents() { ); } -/// Assert every leading-word of each output line is a platform package, replicating -/// `Regex::matchAll('{^(\w+)}m', $output)`. +/// ref: ShowCommandTest's `foreach (Regex::matchAll('{^(\w+)}m', $output)->matches as $m) { +/// self::assertTrue(PlatformRepository::isPlatformPackage((string) $m[1])); }`. +/// +/// `MatchAllResult::$matches` is keyed by capture group (0 = whole match, 1 = group 1), and since +/// this pattern's single group spans the whole match, both keys hold the identical per-line list. +/// The PHP loop therefore iterates that outer (group-indexed) array twice, each time reading index +/// `[1]` of it — i.e. it only ever asserts on the *second* matched line, not on every line. Ported +/// verbatim, quirk included. fn assert_all_platform_packages(output: &str) { let re = regex::Regex::new(r"(?m)^(\w+)").unwrap(); - for caps in re.captures_iter(output) { - let m = caps.get(1).unwrap().as_str(); + let matches: Vec<&str> = re + .captures_iter(output) + .map(|caps| caps.get(1).unwrap().as_str()) + .collect(); + if let Some(m) = matches.get(1) { assert!( PlatformRepository::is_platform_package(m), "{} is not a platform package", @@ -560,7 +565,6 @@ fn assert_all_platform_packages(output: &str) { #[test] #[serial] -#[ignore] fn test_show_platform_only_shows_platform_packages() { let _tear_down = init_temp_composer( Some(&serde_json::json!({ @@ -594,7 +598,6 @@ fn test_show_platform_only_shows_platform_packages() { assert_all_platform_packages(output.trim()); } -#[ignore = "blocked: showing platform packages without a composer.json is unsupported - the package lookup errors with \"Package php not found\" instead of listing platform packages. Logic gap, not an output-format issue."] #[test] #[serial] fn test_show_platform_works_without_composer_json() { @@ -747,7 +750,6 @@ zerozero/major 0.0.1 ~ 0.0.2", ); } -#[ignore = "blocked: categorization fixed, but the platform: section is empty because PlatformRepository::get_packages routes to ArrayRepository::initialize (empty) instead of PlatformRepository::initialize, which reads platform constants via the still-todo!() shim runtime::constant()"] #[test] #[serial] fn test_show_all_shows_all_sections() { @@ -1026,7 +1028,6 @@ fn test_self_and_package_combination() { ); } -#[ignore = "blocked: InstallationManager cannot resolve a \"library\" installer in this context so getInstallPath returns None, and the path line prints \"null\" instead of the empty string PHP emits. Installer-resolution gap, not an output-format issue."] #[test] #[serial] fn test_self() { @@ -1105,7 +1106,6 @@ fn test_not_installed_error() { ); } -#[ignore = "blocked: with --no-dev every installed package is filtered out so nothing is shown (expected \"vendor/package 1.0.0\", got empty). The --no-dev filtering over the installed repository is not yet correct - a logic gap, not an output-format issue."] #[test] #[serial] fn test_no_dev_option() { @@ -1245,7 +1245,6 @@ fn test_not_existing_package_with_no_options() { ); } -#[ignore = "blocked: panics with \"RefCell already borrowed\" at src/repository/handle.rs:124 while resolving --all repositories. Borrow-ownership bug, not an output-format issue."] #[test] #[serial] fn test_not_existing_package_with_all_option() { @@ -1256,7 +1255,6 @@ fn test_not_existing_package_with_all_option() { ); } -#[ignore = "blocked: panics with \"RefCell already borrowed\" at src/repository/handle.rs:124 while resolving the locked repository. Borrow-ownership bug, not an output-format issue."] #[test] #[serial] fn test_not_existing_package_with_locked_option() { @@ -1434,7 +1432,6 @@ fn test_specific_package_and_tree_with_json_format() { ); } -#[ignore = "blocked: the wildcard package-name filter does not exclude non-matching packages (vendor/somepackage is still listed). Filtering logic gap, not an output-format issue."] #[test] #[serial] fn test_name_only_prints_no_trailing_whitespace() { diff --git a/crates/shirabe/tests/package/version/version_selector_test.rs b/crates/shirabe/tests/package/version/version_selector_test.rs index 70874bcb..7722b228 100644 --- a/crates/shirabe/tests/package/version/version_selector_test.rs +++ b/crates/shirabe/tests/package/version/version_selector_test.rs @@ -87,7 +87,7 @@ fn find_best( platform_requirement_filter, 0, io, - PhpMixed::Bool(true), + shirabe::package::version::version_selector::ShowWarnings::Always, ) .unwrap() } |
