diff options
Diffstat (limited to 'crates/shirabe/src/package')
27 files changed, 309 insertions, 178 deletions
diff --git a/crates/shirabe/src/package/alias_package.rs b/crates/shirabe/src/package/alias_package.rs index cd2e5f1..e115362 100644 --- a/crates/shirabe/src/package/alias_package.rs +++ b/crates/shirabe/src/package/alias_package.rs @@ -38,12 +38,12 @@ pub struct AliasPackage { pub(crate) requires: IndexMap<String, Link>, /// @var Link[] pub(crate) dev_requires: IndexMap<String, Link>, - /// @var Link[] - pub(crate) conflicts: Vec<Link>, - /// @var Link[] - pub(crate) provides: Vec<Link>, - /// @var Link[] - pub(crate) replaces: Vec<Link>, + /// @var array<string, Link> + pub(crate) conflicts: IndexMap<String, Link>, + /// @var array<string, Link> + pub(crate) provides: IndexMap<String, Link>, + /// @var array<string, Link> + pub(crate) replaces: IndexMap<String, Link>, } impl AliasPackage { @@ -69,9 +69,9 @@ impl AliasPackage { alias_of, requires: IndexMap::new(), dev_requires: IndexMap::new(), - conflicts: vec![], - provides: vec![], - replaces: vec![], + conflicts: IndexMap::new(), + provides: IndexMap::new(), + replaces: IndexMap::new(), }; for r#type in Link::types() { @@ -101,9 +101,24 @@ impl AliasPackage { .map(|l| (l.get_target().to_string(), l)) .collect(); } - Link::TYPE_PROVIDE => this.provides = replaced, - Link::TYPE_CONFLICT => this.conflicts = replaced, - Link::TYPE_REPLACE => this.replaces = replaced, + Link::TYPE_PROVIDE => { + this.provides = replaced + .into_iter() + .map(|l| (l.get_target().to_string(), l)) + .collect() + } + Link::TYPE_CONFLICT => { + this.conflicts = replaced + .into_iter() + .map(|l| (l.get_target().to_string(), l)) + .collect() + } + Link::TYPE_REPLACE => { + this.replaces = replaced + .into_iter() + .map(|l| (l.get_target().to_string(), l)) + .collect() + } _ => {} } } @@ -202,7 +217,7 @@ impl std::fmt::Display for AliasPackage { write!( f, "{} ({}alias of {})", - self.inner, + self.alias_of, if self.root_package_alias { "root " } else { "" }, self.alias_of.get_version(), ) @@ -210,24 +225,28 @@ impl std::fmt::Display for AliasPackage { } impl PackageInterface for AliasPackage { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn get_name(&self) -> &str { - self.inner.get_name() + self.alias_of.get_name() } fn get_pretty_name(&self) -> &str { - self.inner.get_pretty_name() + self.alias_of.get_pretty_name() } fn get_names(&self, provides: bool) -> Vec<String> { - self.inner.get_names(provides) + self.alias_of.get_names(provides) } fn set_id(&mut self, id: i64) { - self.inner.set_id(id); + self.alias_of.set_id(id); } fn get_id(&self) -> i64 { - self.inner.get_id() + self.alias_of.get_id() } fn is_dev(&self) -> bool { @@ -251,20 +270,20 @@ impl PackageInterface for AliasPackage { } /// @inheritDoc - /// @return array<string|int, Link> - fn get_conflicts(&self) -> Vec<Link> { + /// @return array<string, Link> + fn get_conflicts(&self) -> IndexMap<String, Link> { self.conflicts.clone() } /// @inheritDoc - /// @return array<string|int, Link> - fn get_provides(&self) -> Vec<Link> { + /// @return array<string, Link> + fn get_provides(&self) -> IndexMap<String, Link> { self.provides.clone() } /// @inheritDoc - /// @return array<string|int, Link> - fn get_replaces(&self) -> Vec<Link> { + /// @return array<string, Link> + fn get_replaces(&self) -> IndexMap<String, Link> { self.replaces.clone() } @@ -410,25 +429,25 @@ impl PackageInterface for AliasPackage { fn get_full_pretty_version(&self, truncate: bool, display_mode: i64) -> String { // TODO(phase-b): BasePackage.get_full_pretty_version returns Result; bridge here - self.inner + self.alias_of .get_full_pretty_version(truncate, display_mode) .unwrap_or_default() } fn get_unique_name(&self) -> String { - self.inner.get_unique_name() + self.alias_of.get_unique_name() } fn get_pretty_string(&self) -> String { - self.inner.get_pretty_string() + self.alias_of.get_pretty_string() } fn set_repository(&mut self, repository: Box<dyn RepositoryInterface>) -> anyhow::Result<()> { - self.inner.set_repository(repository) + self.alias_of.set_repository(repository) } fn get_repository(&self) -> Option<&dyn RepositoryInterface> { - self.inner.get_repository() + self.alias_of.get_repository() } } @@ -469,10 +488,6 @@ impl BasePackage for AliasPackage { todo!() } - fn as_any(&self) -> &dyn std::any::Any { - todo!() - } - fn clone_box(&self) -> Box<dyn BasePackage> { todo!() } diff --git a/crates/shirabe/src/package/archiver/archivable_files_finder.rs b/crates/shirabe/src/package/archiver/archivable_files_finder.rs index 35ec36f..72ffabf 100644 --- a/crates/shirabe/src/package/archiver/archivable_files_finder.rs +++ b/crates/shirabe/src/package/archiver/archivable_files_finder.rs @@ -23,7 +23,7 @@ impl std::fmt::Debug for ArchivableFilesFinder { impl ArchivableFilesFinder { pub fn new(sources: &str, excludes: Vec<String>, ignore_filters: bool) -> anyhow::Result<Self> { - let fs = Filesystem::new(); + let fs = Filesystem::new(None); let sources_real_path = realpath(sources); if sources_real_path.is_none() { @@ -39,8 +39,8 @@ impl ArchivableFilesFinder { vec![] } else { vec![ - Box::new(GitExcludeFilter::new(&sources)), - Box::new(ComposerExcludeFilter::new(&sources, excludes)), + Box::new(GitExcludeFilter::new(sources.clone())), + Box::new(ComposerExcludeFilter::new(sources.clone(), excludes)), ] }; @@ -61,7 +61,8 @@ impl ArchivableFilesFinder { &format!("^{}", preg_quote(&sources_clone, Some('#'))), "", &fs.normalize_path(&realpath), - ); + ) + .unwrap_or_default(); let mut exclude = false; for f in &filters { @@ -72,13 +73,14 @@ impl ArchivableFilesFinder { }; finder - .in_dir(&sources) - .filter(Box::new(filter)) + .r#in(&sources) + // TODO(phase-b): symfony Finder filter takes Box<dyn Fn(&SplFileInfo) -> bool>; signature not yet wired .ignore_vcs(true) .ignore_dot_files(false) .sort_by_name(); + let _ = filter; - let inner_iter = finder.get_iterator(); + let inner_iter: Box<dyn Iterator<Item = SplFileInfo>> = Box::new(finder.get_iterator()); Ok(Self { finder, inner_iter }) } @@ -88,7 +90,7 @@ impl ArchivableFilesFinder { return true; } - let path = current.to_string(); + let path = current.get_pathname(); match std::fs::read_dir(&path) { Ok(mut iter) => iter.next().is_none(), Err(_) => false, diff --git a/crates/shirabe/src/package/archiver/archive_manager.rs b/crates/shirabe/src/package/archiver/archive_manager.rs index 3f7bfe0..73b0d84 100644 --- a/crates/shirabe/src/package/archiver/archive_manager.rs +++ b/crates/shirabe/src/package/archiver/archive_manager.rs @@ -19,8 +19,8 @@ use crate::util::r#loop::Loop; use crate::util::sync_helper::SyncHelper; pub struct ArchiveManager { - pub(crate) download_manager: DownloadManager, - pub(crate) r#loop: Loop, + pub(crate) download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>, + pub(crate) r#loop: std::rc::Rc<std::cell::RefCell<Loop>>, pub(crate) archivers: Vec<Box<dyn ArchiverInterface>>, pub(crate) overwrite_files: bool, } @@ -34,7 +34,10 @@ impl std::fmt::Debug for ArchiveManager { } impl ArchiveManager { - pub fn new(download_manager: DownloadManager, r#loop: Loop) -> Self { + pub fn new( + download_manager: std::rc::Rc<std::cell::RefCell<DownloadManager>>, + r#loop: std::rc::Rc<std::cell::RefCell<Loop>>, + ) -> Self { Self { download_manager, r#loop, @@ -144,7 +147,7 @@ impl ArchiveManager { } }; - let filesystem = Filesystem::new(); + let filesystem = Filesystem::new(None); let is_root = package.as_any().is::<dyn RootPackageInterface>(); let source_path: String; @@ -158,10 +161,16 @@ impl ArchiveManager { filesystem.ensure_directory_exists(&source_path)?; let download_result = (|| -> anyhow::Result<()> { - let promise = self.download_manager.download(package, &source_path)?; - SyncHelper::r#await(&self.r#loop, promise)?; - let promise = self.download_manager.install(package, &source_path)?; - SyncHelper::r#await(&self.r#loop, promise)?; + let promise = + self.download_manager + .borrow() + .download(package, &source_path, None)?; + SyncHelper::r#await(&self.r#loop, Some(promise))?; + let promise = self + .download_manager + .borrow() + .install(package, &source_path)?; + SyncHelper::r#await(&self.r#loop, Some(promise))?; Ok(()) })(); @@ -172,7 +181,7 @@ impl ArchiveManager { let composer_json_path = format!("{}/composer.json", source_path); if file_exists(&composer_json_path) { - let json_file = JsonFile::new(composer_json_path, None, None); + let json_file = JsonFile::new(composer_json_path, None, None)?; let json_data = json_file.read()?; if let Some(archive) = json_data.get("archive") { if let Some(name) = archive.get("name").and_then(|v| v.as_str()) { diff --git a/crates/shirabe/src/package/archiver/base_exclude_filter.rs b/crates/shirabe/src/package/archiver/base_exclude_filter.rs index c7d2557..1a716d0 100644 --- a/crates/shirabe/src/package/archiver/base_exclude_filter.rs +++ b/crates/shirabe/src/package/archiver/base_exclude_filter.rs @@ -59,7 +59,7 @@ impl BaseExcludeFilterBase { let rule = rule.trim_matches('/'); - let glob_regex = Glob::to_regex(rule); + let glob_regex = Glob::to_regex(rule, true, true); let rule_regex = &glob_regex[2..glob_regex.len() - 2]; ( @@ -143,7 +143,7 @@ pub trait BaseExcludeFilter { let rule = rule.trim_matches('/'); // remove delimiters as well as caret (^) and dollar sign ($) from the regex - let glob_regex = Glob::to_regex(rule); + let glob_regex = Glob::to_regex(rule, true, true); let rule_regex = &glob_regex[2..glob_regex.len() - 2]; ( diff --git a/crates/shirabe/src/package/archiver/git_exclude_filter.rs b/crates/shirabe/src/package/archiver/git_exclude_filter.rs index dddad12..1a0ec4d 100644 --- a/crates/shirabe/src/package/archiver/git_exclude_filter.rs +++ b/crates/shirabe/src/package/archiver/git_exclude_filter.rs @@ -34,14 +34,17 @@ impl GitExcludeFilter { } fn parse_git_attributes_line_static(line: &str) -> Option<(String, bool, bool)> { - let parts = Preg::split(r"\s+", line); + let parts = Preg::split(r"\s+", line).unwrap_or_default(); if parts.len() == 2 && parts[1] == "export-ignore" { return Some(BaseExcludeFilterBase::generate_pattern(&parts[0])); } if parts.len() == 2 && parts[1] == "-export-ignore" { - return BaseExcludeFilterBase::generate_pattern(&format!("!{}", parts[0])); + return Some(BaseExcludeFilterBase::generate_pattern(&format!( + "!{}", + parts[0] + ))); } None diff --git a/crates/shirabe/src/package/archiver/phar_archiver.rs b/crates/shirabe/src/package/archiver/phar_archiver.rs index 2e9d96e..7b5142b 100644 --- a/crates/shirabe/src/package/archiver/phar_archiver.rs +++ b/crates/shirabe/src/package/archiver/phar_archiver.rs @@ -29,6 +29,12 @@ fn compress_formats() -> IndexMap<&'static str, i64> { #[derive(Debug)] pub struct PharArchiver; +impl PharArchiver { + pub fn new() -> Self { + Self + } +} + impl ArchiverInterface for PharArchiver { fn archive( &self, @@ -46,6 +52,7 @@ impl ArchiverInterface for PharArchiver { unlink(&target); } + let target_outer = target.clone(); let inner = (|| -> anyhow::Result<String> { let pos = strrpos(&target, &format).unwrap_or(target.len()); let filename = target[..pos.saturating_sub(1)].to_string(); @@ -63,7 +70,10 @@ impl ArchiverInterface for PharArchiver { *formats.get(format.as_str()).unwrap_or(&Phar::TAR), ); let files = ArchivableFilesFinder::new(&sources, excludes, ignore_filters)?; - let mut files_only = ArchivableFilesFilter::new(files); + // TODO(phase-b): unify iterator types (ArchivableFilesFinder yields SplFileInfo, + // ArchivableFilesFilter expects PathBuf). + let mut files_only = + ArchivableFilesFilter::new(Box::new(files.map(|f| f.get_pathname().into()))); phar.build_from_iterator(&mut files_only, &sources); files_only.add_empty_dir(&phar, &sources); @@ -137,7 +147,7 @@ impl ArchiverInterface for PharArchiver { inner.map_err(|e| { let message = format!( "Could not create archive '{}' from '{}': {}", - target, sources, e + target_outer, sources, e ); anyhow::anyhow!(RuntimeException { message, code: 0 }) }) diff --git a/crates/shirabe/src/package/archiver/zip_archiver.rs b/crates/shirabe/src/package/archiver/zip_archiver.rs index ef5b40a..471352f 100644 --- a/crates/shirabe/src/package/archiver/zip_archiver.rs +++ b/crates/shirabe/src/package/archiver/zip_archiver.rs @@ -13,6 +13,10 @@ use shirabe_php_shim::{ pub struct ZipArchiver; impl ZipArchiver { + pub fn new() -> Self { + Self + } + fn formats() -> IndexMap<String, bool> { let mut map = IndexMap::new(); map.insert("zip".to_string(), true); @@ -33,7 +37,7 @@ impl ArchiverInterface for ZipArchiver { excludes: Vec<String>, ignore_filters: bool, ) -> anyhow::Result<String> { - let fs = Filesystem::new(); + let fs = Filesystem::new(None); let sources_realpath = realpath(&sources); let sources = if let Some(p) = sources_realpath { p @@ -47,7 +51,7 @@ impl ArchiverInterface for ZipArchiver { let files = ArchivableFilesFinder::new(&sources, excludes, ignore_filters)?; for file in files { let filepath = file.get_pathname(); - let mut relative_path = file.get_relative_pathname(); + let mut relative_path = file.get_relative_path_name(); if Platform::is_windows() { relative_path = shirabe_php_shim::strtr(&relative_path, "\\", "/"); diff --git a/crates/shirabe/src/package/base_package.rs b/crates/shirabe/src/package/base_package.rs index 2e50c44..fb906d1 100644 --- a/crates/shirabe/src/package/base_package.rs +++ b/crates/shirabe/src/package/base_package.rs @@ -83,62 +83,20 @@ pub trait BasePackage: PackageInterface + std::fmt::Display { fn set_repository_box(&mut self, repository: Box<dyn RepositoryInterface>); fn take_repository(&mut self) -> Option<Box<dyn RepositoryInterface>>; - fn as_any(&self) -> &dyn std::any::Any; fn clone_box(&self) -> Box<dyn BasePackage>; - fn get_name(&self) -> &str { - self.name() - } - - fn get_pretty_name(&self) -> &str { - self.pretty_name() - } - - fn get_names(&self, provides: bool) -> Vec<String> { - let mut names: IndexMap<String, bool> = IndexMap::new(); - names.insert(self.get_name().to_string(), true); - - if provides { - for link in self.get_provides().values() { - names.insert(link.get_target().to_string(), true); - } - } - - for link in self.get_replaces().values() { - names.insert(link.get_target().to_string(), true); - } - - names.into_keys().collect() - } - - fn set_id(&mut self, id: i64) { - *self.id_mut() = id; - } + // as_alias_package / as_complete_package_interface inherited from PackageInterface. - fn get_id(&self) -> i64 { - self.id() + fn as_alias_package_mut(&mut self) -> Option<&mut crate::package::alias_package::AliasPackage> { + None } - fn set_repository(&mut self, repository: Box<dyn RepositoryInterface>) -> anyhow::Result<()> { - if let Some(existing) = self.repository_opt() { - // TODO(phase-b): proper reference identity check before raising error - return Err(anyhow::anyhow!(LogicException { - message: format!( - "Package \"{}\" cannot be added to repository \"{}\" as it is already in repository \"{}\".", - self.get_pretty_name(), - repository.get_repo_name(), - existing.get_repo_name(), - ), - code: 0, - })); - } - self.set_repository_box(repository); - Ok(()) - } + // get_name / get_pretty_name / get_names live on PackageInterface; the BasePackage + // duplicates were causing ambiguity at every call site (`pkg.get_name()` with + // pkg: &dyn BasePackage). Concrete impls already forward to name()/pretty_name(). - fn get_repository(&self) -> Option<&dyn RepositoryInterface> { - self.repository_opt() - } + // set_id, get_id, get_repository, get_unique_name, set_repository are inherited + // from PackageInterface; do not redeclare here to avoid trait-method ambiguity. fn is_platform(&self) -> bool { self.repository_opt() @@ -146,24 +104,18 @@ pub trait BasePackage: PackageInterface + std::fmt::Display { .is_some() } - fn get_unique_name(&self) -> String { - format!("{}-{}", self.get_name(), self.get_version()) - } - fn equals(&self, _package: &dyn PackageInterface) -> bool { // TODO(phase-b): implement via reference identity (requires Rc/Arc) // PHP uses === which is reference equality; unwraps AliasPackage on both sides todo!("equals requires reference identity which needs Rc/Arc") } - fn get_pretty_string(&self) -> String { - format!("{} {}", self.get_pretty_name(), self.get_pretty_version()) - } + // get_pretty_string is inherited from PackageInterface. fn get_full_pretty_version(&self, truncate: bool, display_mode: i64) -> anyhow::Result<String> { - const DISPLAY_SOURCE_REF_IF_DEV: i64 = PackageInterface::DISPLAY_SOURCE_REF_IF_DEV; - const DISPLAY_SOURCE_REF: i64 = PackageInterface::DISPLAY_SOURCE_REF; - const DISPLAY_DIST_REF: i64 = PackageInterface::DISPLAY_DIST_REF; + const DISPLAY_SOURCE_REF_IF_DEV: i64 = <dyn PackageInterface>::DISPLAY_SOURCE_REF_IF_DEV; + const DISPLAY_SOURCE_REF: i64 = <dyn PackageInterface>::DISPLAY_SOURCE_REF; + const DISPLAY_DIST_REF: i64 = <dyn PackageInterface>::DISPLAY_DIST_REF; if display_mode == DISPLAY_SOURCE_REF_IF_DEV && (!self.is_dev() @@ -207,7 +159,7 @@ pub trait BasePackage: PackageInterface + std::fmt::Display { fn get_stability_priority(&self) -> i64 { *STABILITIES .get(self.get_stability()) - .unwrap_or(&Self::STABILITY_STABLE) + .unwrap_or(&STABILITY_STABLE) } fn php_clone(&mut self) { diff --git a/crates/shirabe/src/package/comparer/comparer.rs b/crates/shirabe/src/package/comparer/comparer.rs index 0e0d295..8f406a5 100644 --- a/crates/shirabe/src/package/comparer/comparer.rs +++ b/crates/shirabe/src/package/comparer/comparer.rs @@ -65,7 +65,7 @@ impl Comparer { let mut source: IndexMap<String, IndexMap<String, Option<String>>> = IndexMap::new(); let mut destination: IndexMap<String, IndexMap<String, Option<String>>> = IndexMap::new(); self.changed = IndexMap::new(); - let current_directory = Platform::get_cwd(); + let current_directory = Platform::get_cwd(false).unwrap_or_default(); shirabe_php_shim::chdir(&self.source); if !Self::do_tree(".", &mut source) { return; diff --git a/crates/shirabe/src/package/complete_alias_package.rs b/crates/shirabe/src/package/complete_alias_package.rs index 134ed3a..d187eea 100644 --- a/crates/shirabe/src/package/complete_alias_package.rs +++ b/crates/shirabe/src/package/complete_alias_package.rs @@ -13,7 +13,13 @@ pub struct CompleteAliasPackage { impl CompleteAliasPackage { pub fn new(alias_of: CompletePackage, version: String, pretty_version: String) -> Self { - let inner = AliasPackage::new(alias_of.clone(), version, pretty_version); + // TODO(phase-b): alias_of is a PHP class (shared semantics); cloning is wrong. + // Use a dummy BasePackage placeholder until the field is migrated to Rc<CompletePackage>. + let inner = AliasPackage::new( + todo!("share CompletePackage via Rc"), + version, + pretty_version, + ); Self { inner, alias_of } } @@ -61,7 +67,8 @@ impl CompleteAliasPackage { } pub fn set_description(&mut self, description: Option<String>) { - self.alias_of.set_description(description); + self.alias_of + .set_description(description.unwrap_or_default()); } pub fn get_homepage(&self) -> Option<&str> { @@ -69,7 +76,7 @@ impl CompleteAliasPackage { } pub fn set_homepage(&mut self, homepage: Option<String>) { - self.alias_of.set_homepage(homepage); + self.alias_of.set_homepage(homepage.unwrap_or_default()); } pub fn get_authors(&self) -> Vec<indexmap::IndexMap<String, String>> { @@ -116,7 +123,7 @@ impl CompleteAliasPackage { } pub fn set_archive_name(&mut self, name: Option<String>) { - self.alias_of.set_archive_name(name); + self.alias_of.set_archive_name(name.unwrap_or_default()); } pub fn get_archive_excludes(&self) -> Vec<String> { diff --git a/crates/shirabe/src/package/complete_package.rs b/crates/shirabe/src/package/complete_package.rs index a3db372..27c49c6 100644 --- a/crates/shirabe/src/package/complete_package.rs +++ b/crates/shirabe/src/package/complete_package.rs @@ -133,6 +133,10 @@ impl CompletePackageInterface for CompletePackage { } impl PackageInterface for CompletePackage { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn get_name(&self) -> &str { todo!() } @@ -253,15 +257,15 @@ impl PackageInterface for CompletePackage { todo!() } - fn get_conflicts(&self) -> Vec<super::link::Link> { + fn get_conflicts(&self) -> IndexMap<String, super::link::Link> { todo!() } - fn get_provides(&self) -> Vec<super::link::Link> { + fn get_provides(&self) -> IndexMap<String, super::link::Link> { todo!() } - fn get_replaces(&self) -> Vec<super::link::Link> { + fn get_replaces(&self) -> IndexMap<String, super::link::Link> { todo!() } diff --git a/crates/shirabe/src/package/complete_package_interface.rs b/crates/shirabe/src/package/complete_package_interface.rs index 31780a7..d1120ac 100644 --- a/crates/shirabe/src/package/complete_package_interface.rs +++ b/crates/shirabe/src/package/complete_package_interface.rs @@ -55,4 +55,8 @@ pub trait CompletePackageInterface: PackageInterface { fn get_archive_excludes(&self) -> Vec<String>; fn set_archive_excludes(&mut self, excludes: Vec<String>); + + fn as_package_interface(&self) -> &dyn crate::package::package_interface::PackageInterface { + todo!() + } } diff --git a/crates/shirabe/src/package/dumper/array_dumper.rs b/crates/shirabe/src/package/dumper/array_dumper.rs index 02f1092..d81fd6c 100644 --- a/crates/shirabe/src/package/dumper/array_dumper.rs +++ b/crates/shirabe/src/package/dumper/array_dumper.rs @@ -131,8 +131,10 @@ impl ArrayDumper { } // corresponds to: foreach (BasePackage::$supportedLinkTypes as $type => $opts) { $links = $package->{'get'.ucfirst($opts['method'])}(); ... } - for (type_name, method_name) in BasePackage::supported_link_types() { - let links = package.get_links_by_method(&method_name); + for (type_name, method_name) in <dyn BasePackage>::supported_link_types() { + // TODO(phase-b): PackageInterface needs get_links_by_method to mimic PHP magic call + let links: Vec<crate::package::link::Link> = Vec::new(); + let _ = (&method_name, package); if links.is_empty() { continue; } @@ -186,10 +188,9 @@ impl ArrayDumper { ), ); } - if let Some(pkg_type) = package.get_type() { - if !pkg_type.is_empty() { - data.insert("type".to_string(), PhpMixed::String(pkg_type.to_string())); - } + let pkg_type = package.get_type(); + if !pkg_type.is_empty() { + data.insert("type".to_string(), PhpMixed::String(pkg_type.to_string())); } let extra = package.get_extra(); if !extra.is_empty() { @@ -247,10 +248,15 @@ impl ArrayDumper { ); } let php_ext = package.get_php_ext(); - if !php_ext.is_empty() { + if let Some(php_ext) = php_ext.as_ref().filter(|m| !m.is_empty()) { data.insert( "php-ext".to_string(), - PhpMixed::Array(php_ext.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), + PhpMixed::Array( + php_ext + .iter() + .map(|(k, v)| (k.clone(), Box::new(v.clone()))) + .collect(), + ), ); } diff --git a/crates/shirabe/src/package/link.rs b/crates/shirabe/src/package/link.rs index 2233eb1..e41d431 100644 --- a/crates/shirabe/src/package/link.rs +++ b/crates/shirabe/src/package/link.rs @@ -13,6 +13,20 @@ pub struct Link { pub(crate) pretty_constraint: Option<String>, } +impl Clone for Link { + fn clone(&self) -> Self { + // TODO(phase-b): Link is a PHP class; this clone is a shallow placeholder until + // Link is shared via Rc<Link>. + Self { + source: self.source.clone(), + target: self.target.clone(), + constraint: self.constraint.clone_box(), + description: self.description.clone(), + pretty_constraint: self.pretty_constraint.clone(), + } + } +} + impl std::fmt::Debug for Link { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Link") diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs index 28e13af..275d718 100644 --- a/crates/shirabe/src/package/loader/array_loader.rs +++ b/crates/shirabe/src/package/loader/array_loader.rs @@ -588,7 +588,7 @@ impl ArrayLoader { let alias_normalized = self.get_branch_alias(config)?; if let Some(alias_normalized) = alias_normalized { if !alias_normalized.is_empty() { - let pretty_alias = Preg::replace(r"{(\.9{7})+}", ".x", &alias_normalized); + let pretty_alias = Preg::replace(r"{(\.9{7})+}", ".x", &alias_normalized)?; // TODO(phase-b): `$package instanceof RootPackage` downcast from CompletePackage let package_as_root: Option<RootPackage> = None; diff --git a/crates/shirabe/src/package/loader/invalid_package_exception.rs b/crates/shirabe/src/package/loader/invalid_package_exception.rs index 96725ba..764b31f 100644 --- a/crates/shirabe/src/package/loader/invalid_package_exception.rs +++ b/crates/shirabe/src/package/loader/invalid_package_exception.rs @@ -1,5 +1,6 @@ //! ref: composer/src/Composer/Package/Loader/InvalidPackageException.php +use indexmap::IndexMap; use shirabe_php_shim::{Exception, PhpMixed}; #[derive(Debug)] @@ -7,11 +8,15 @@ pub struct InvalidPackageException { inner: Exception, errors: Vec<String>, warnings: Vec<String>, - data: Vec<PhpMixed>, + data: IndexMap<String, PhpMixed>, } impl InvalidPackageException { - pub fn new(errors: Vec<String>, warnings: Vec<String>, data: Vec<PhpMixed>) -> Self { + pub fn new( + errors: Vec<String>, + warnings: Vec<String>, + data: IndexMap<String, PhpMixed>, + ) -> Self { let message = format!( "Invalid package information: \n{}", errors @@ -29,7 +34,7 @@ impl InvalidPackageException { } } - pub fn get_data(&self) -> &[PhpMixed] { + pub fn get_data(&self) -> &IndexMap<String, PhpMixed> { &self.data } diff --git a/crates/shirabe/src/package/loader/json_loader.rs b/crates/shirabe/src/package/loader/json_loader.rs index 978bc28..cb3cbeb 100644 --- a/crates/shirabe/src/package/loader/json_loader.rs +++ b/crates/shirabe/src/package/loader/json_loader.rs @@ -22,13 +22,16 @@ impl JsonLoader { pub fn load(&self, json: JsonLoaderInput) -> Result<Box<dyn BasePackage>> { let config = match json { - JsonLoaderInput::File(json_file) => json_file.read()?, + JsonLoaderInput::File(mut json_file) => json_file.read()?, JsonLoaderInput::String(ref s) if Path::new(s).exists() => { - JsonFile::parse_json(&std::fs::read_to_string(s)?, Some(s))? + let contents = std::fs::read_to_string(s)?; + JsonFile::parse_json(Some(&contents), Some(s))? } - JsonLoaderInput::String(ref s) => JsonFile::parse_json(s, None)?, + JsonLoaderInput::String(ref s) => JsonFile::parse_json(Some(s), None)?, }; - self.loader.load(config, None) + // TODO(phase-b): JsonFile::parse_json returns PhpMixed; loader::load expects IndexMap + let _ = config; + self.loader.load(indexmap::IndexMap::new(), None) } } diff --git a/crates/shirabe/src/package/loader/validating_array_loader.rs b/crates/shirabe/src/package/loader/validating_array_loader.rs index 3ba9212..5009a0d 100644 --- a/crates/shirabe/src/package/loader/validating_array_loader.rs +++ b/crates/shirabe/src/package/loader/validating_array_loader.rs @@ -1288,7 +1288,10 @@ impl ValidatingArrayLoader { return Err(anyhow::anyhow!(InvalidPackageException::new( self.errors.clone(), self.warnings.clone(), - config.values().map(|v| (**v).clone()).collect(), + config + .iter() + .map(|(k, v)| (k.clone(), (**v).clone())) + .collect(), ))); } diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs index 5950437..397af08 100644 --- a/crates/shirabe/src/package/locker.rs +++ b/crates/shirabe/src/package/locker.rs @@ -134,7 +134,6 @@ impl Locker { .collect(), ), 0, - JsonFile::INDENT_DEFAULT, ), )) } @@ -616,13 +615,13 @@ impl Locker { } else { self.virtual_file_written = true; self.lock_data_cache = Some(JsonFile::parse_json( - &JsonFile::encode( + Some(&JsonFile::encode_with_indent( &PhpMixed::Array(lock.into_iter().map(|(k, v)| (k, Box::new(v))).collect()), shirabe_php_shim::JSON_UNESCAPED_SLASHES | shirabe_php_shim::JSON_PRETTY_PRINT | shirabe_php_shim::JSON_UNESCAPED_UNICODE, JsonFile::INDENT_DEFAULT, - ), + )), None, )?); } diff --git a/crates/shirabe/src/package/package.rs b/crates/shirabe/src/package/package.rs index b8b6770..74286a0 100644 --- a/crates/shirabe/src/package/package.rs +++ b/crates/shirabe/src/package/package.rs @@ -71,7 +71,10 @@ impl Package { let stability = VersionParser::parse_stability(&version).to_string(); let dev = stability == "dev"; Self { - inner: BasePackage::new(name), + id: -1, + name: name.to_lowercase(), + pretty_name: name, + repository: None, r#type: None, target_dir: None, installation_source: None, @@ -458,7 +461,7 @@ impl Package { let url = if url_type == "dist" && strpos(url, "%").is_some() { ComposerMirror::process_url( url, - &self.inner.name, + &self.name, &self.version, r#ref.unwrap_or(""), r#type.unwrap_or(""), @@ -474,7 +477,7 @@ impl Package { let mirror_url = if url_type == "dist" { ComposerMirror::process_url( &mirror.url, - &self.inner.name, + &self.name, &self.version, r#ref.unwrap_or(""), r#type.unwrap_or(""), @@ -483,14 +486,14 @@ impl Package { } else if url_type == "source" && r#type == Some("git") { ComposerMirror::process_git_url( &mirror.url, - &self.inner.name, + &self.name, &url, r#type.unwrap_or(""), ) } else if url_type == "source" && r#type == Some("hg") { ComposerMirror::process_hg_url( &mirror.url, - &self.inner.name, + &self.name, &url, r#type.unwrap_or(""), ) @@ -568,10 +571,6 @@ impl BasePackage for Package { todo!() } - fn as_any(&self) -> &dyn std::any::Any { - todo!() - } - fn clone_box(&self) -> Box<dyn BasePackage> { todo!() } @@ -584,6 +583,9 @@ impl std::fmt::Display for Package { } impl PackageInterface for Package { + fn as_any(&self) -> &dyn std::any::Any { + self + } fn get_name(&self) -> &str { todo!() } @@ -674,13 +676,13 @@ impl PackageInterface for Package { fn get_requires(&self) -> IndexMap<String, Link> { todo!() } - fn get_conflicts(&self) -> Vec<Link> { + fn get_conflicts(&self) -> IndexMap<String, Link> { todo!() } - fn get_provides(&self) -> Vec<Link> { + fn get_provides(&self) -> IndexMap<String, Link> { todo!() } - fn get_replaces(&self) -> Vec<Link> { + fn get_replaces(&self) -> IndexMap<String, Link> { todo!() } fn get_dev_requires(&self) -> IndexMap<String, Link> { diff --git a/crates/shirabe/src/package/package_interface.rs b/crates/shirabe/src/package/package_interface.rs index 6a07556..c6ccce8 100644 --- a/crates/shirabe/src/package/package_interface.rs +++ b/crates/shirabe/src/package/package_interface.rs @@ -14,7 +14,9 @@ use crate::repository::repository_interface::RepositoryInterface; /// @phpstan-type AutoloadRules array{psr-0?: array<string, string|string[]>, psr-4?: array<string, string|string[]>, classmap?: list<string>, files?: list<string>, exclude-from-classmap?: list<string>} /// @phpstan-type DevAutoloadRules array{psr-0?: array<string, string|string[]>, psr-4?: array<string, string|string[]>, classmap?: list<string>, files?: list<string>} /// @phpstan-type PhpExtConfig array{extension-name?: string, priority?: int, support-zts?: bool, support-nts?: bool, build-path?: string|null, download-url-method?: string|list<string>, os-families?: non-empty-list<non-empty-string>, os-families-exclude?: non-empty-list<non-empty-string>, configure-options?: list<array{name: string, description?: string}>} -pub trait PackageInterface: std::fmt::Display { +pub trait PackageInterface: std::fmt::Display + std::fmt::Debug { + fn as_any(&self) -> &dyn std::any::Any; + /// Returns the package's name without version info, thus not a unique identifier /// /// @return string package name @@ -170,20 +172,20 @@ pub trait PackageInterface: std::fmt::Display { /// Returns a set of links to packages which must not be installed at the /// same time as this package /// - /// @return Link[] An array of package links defining conflicting packages - fn get_conflicts(&self) -> Vec<Link>; + /// @return array<string, Link> A map of package links defining conflicting packages + fn get_conflicts(&self) -> IndexMap<String, Link>; /// Returns a set of links to virtual packages that are provided through /// this package /// - /// @return Link[] An array of package links defining provided packages - fn get_provides(&self) -> Vec<Link>; + /// @return array<string, Link> A map of package links defining provided packages + fn get_provides(&self) -> IndexMap<String, Link>; /// Returns a set of links to packages which can alternatively be /// satisfied by installing this package /// - /// @return Link[] An array of package links defining replaced packages - fn get_replaces(&self) -> Vec<Link>; + /// @return array<string, Link> A map of package links defining replaced packages + fn get_replaces(&self) -> IndexMap<String, Link>; /// Returns a set of links to packages which are required to develop /// this package. These are installed if in dev mode. @@ -275,6 +277,30 @@ pub trait PackageInterface: std::fmt::Display { /// Set dist and source references and update dist URL for ones that contain a reference fn set_source_dist_references(&mut self, reference: &str); + + // clone_box was moved to BasePackage with a Box<dyn BasePackage> return type; + // exposing it here too caused trait-method ambiguity at every BasePackage call site. + // Callers holding `&dyn PackageInterface` (rather than `&dyn BasePackage`) can use + // `clone_package_box` instead. + fn clone_package_box(&self) -> Box<dyn PackageInterface> { + todo!() + } + + fn as_alias_package(&self) -> Option<&crate::package::alias_package::AliasPackage> { + None + } + + fn as_complete_package_interface( + &self, + ) -> Option<&dyn crate::package::complete_package_interface::CompletePackageInterface> { + None + } + + fn as_complete_package( + &self, + ) -> Option<&dyn crate::package::complete_package_interface::CompletePackageInterface> { + None + } } impl dyn PackageInterface { diff --git a/crates/shirabe/src/package/root_alias_package.rs b/crates/shirabe/src/package/root_alias_package.rs index 703c1f9..cb458f3 100644 --- a/crates/shirabe/src/package/root_alias_package.rs +++ b/crates/shirabe/src/package/root_alias_package.rs @@ -231,6 +231,10 @@ impl std::fmt::Display for RootAliasPackage { } impl PackageInterface for RootAliasPackage { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn get_name(&self) -> &str { todo!() } @@ -321,13 +325,13 @@ impl PackageInterface for RootAliasPackage { fn get_requires(&self) -> IndexMap<String, Link> { todo!() } - fn get_conflicts(&self) -> Vec<Link> { + fn get_conflicts(&self) -> IndexMap<String, Link> { todo!() } - fn get_provides(&self) -> Vec<Link> { + fn get_provides(&self) -> IndexMap<String, Link> { todo!() } - fn get_replaces(&self) -> Vec<Link> { + fn get_replaces(&self) -> IndexMap<String, Link> { todo!() } fn get_dev_requires(&self) -> IndexMap<String, Link> { diff --git a/crates/shirabe/src/package/root_package.rs b/crates/shirabe/src/package/root_package.rs index 2557c8a..af8d1f5 100644 --- a/crates/shirabe/src/package/root_package.rs +++ b/crates/shirabe/src/package/root_package.rs @@ -24,6 +24,21 @@ pub struct RootPackage { impl RootPackage { pub const DEFAULT_PRETTY_VERSION: &'static str = "1.0.0+no-version-set"; + + pub fn new(name: String, version: String, pretty_version: String) -> Self { + // TODO(phase-b): CompletePackage::new signature is not yet pinned down + let inner: CompletePackage = todo!(); + let _ = (name, version, pretty_version); + Self { + inner, + minimum_stability: "stable".to_string(), + prefer_stable: false, + stability_flags: IndexMap::new(), + config: IndexMap::new(), + references: IndexMap::new(), + aliases: Vec::new(), + } + } } impl RootPackageInterface for RootPackage { @@ -221,6 +236,10 @@ impl std::fmt::Display for RootPackage { } impl PackageInterface for RootPackage { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn get_name(&self) -> &str { todo!() } @@ -311,13 +330,13 @@ impl PackageInterface for RootPackage { fn get_requires(&self) -> IndexMap<String, Link> { todo!() } - fn get_conflicts(&self) -> Vec<Link> { + fn get_conflicts(&self) -> IndexMap<String, Link> { todo!() } - fn get_provides(&self) -> Vec<Link> { + fn get_provides(&self) -> IndexMap<String, Link> { todo!() } - fn get_replaces(&self) -> Vec<Link> { + fn get_replaces(&self) -> IndexMap<String, Link> { todo!() } fn get_dev_requires(&self) -> IndexMap<String, Link> { diff --git a/crates/shirabe/src/package/root_package_interface.rs b/crates/shirabe/src/package/root_package_interface.rs index a8634af..a053e28 100644 --- a/crates/shirabe/src/package/root_package_interface.rs +++ b/crates/shirabe/src/package/root_package_interface.rs @@ -3,6 +3,8 @@ use indexmap::IndexMap; use shirabe_php_shim::PhpMixed; +use crate::package::package_interface::PackageInterface; + use crate::package::complete_package_interface::CompletePackageInterface; use crate::package::link::Link; @@ -48,4 +50,16 @@ pub trait RootPackageInterface: CompletePackageInterface { fn set_suggests(&mut self, suggests: IndexMap<String, String>); fn set_extra(&mut self, extra: IndexMap<String, PhpMixed>); + + fn clone_as_package_interface(&self) -> Box<dyn PackageInterface> { + todo!() + } + + fn clone_box(&self) -> Box<dyn RootPackageInterface> { + todo!() + } + + fn as_package_interface(&self) -> &dyn PackageInterface { + todo!() + } } diff --git a/crates/shirabe/src/package/version/stability_filter.rs b/crates/shirabe/src/package/version/stability_filter.rs index d08492c..5053e9d 100644 --- a/crates/shirabe/src/package/version/stability_filter.rs +++ b/crates/shirabe/src/package/version/stability_filter.rs @@ -1,6 +1,6 @@ //! ref: composer/src/Composer/Package/Version/StabilityFilter.php -use crate::package::base_package::BasePackage; +use crate::package::base_package::STABILITIES; use indexmap::IndexMap; pub struct StabilityFilter; @@ -15,7 +15,7 @@ impl StabilityFilter { for name in names { // allow if package matches the package-specific stability flag if let Some(&flag) = stability_flags.get(name) { - if let Some(&stability_value) = BasePackage::STABILITIES.get(stability) { + if let Some(&stability_value) = STABILITIES.get(stability) { if stability_value <= flag { return true; } diff --git a/crates/shirabe/src/package/version/version_bumper.rs b/crates/shirabe/src/package/version/version_bumper.rs index 5911458..8963fd6 100644 --- a/crates/shirabe/src/package/version/version_bumper.rs +++ b/crates/shirabe/src/package/version/version_bumper.rs @@ -26,11 +26,15 @@ impl VersionBumper { return Ok(pretty_constraint); } - let mut version = package.get_version(); - if package.get_version().starts_with("dev-") { - let loader = ArrayLoader::new(&parser); + let mut version = package.get_version().to_string(); + if version.starts_with("dev-") { + // TODO(phase-b): ArrayLoader::new takes Option<VersionParser> by value; pass None until + // VersionParser sharing is reconciled. + let _ = &parser; + let loader = ArrayLoader::new(None, false); let dumper = ArrayDumper::new(); - let extra = loader.get_branch_alias(dumper.dump(package)); + let dumped = dumper.dump(package); + let extra = loader.get_branch_alias(&dumped)?; if extra.is_none() || extra.as_deref() == Some(VersionParser::DEFAULT_BRANCH_ALIAS) { return Ok(pretty_constraint); diff --git a/crates/shirabe/src/package/version/version_parser.rs b/crates/shirabe/src/package/version/version_parser.rs index 6bb1004..6dbf425 100644 --- a/crates/shirabe/src/package/version/version_parser.rs +++ b/crates/shirabe/src/package/version/version_parser.rs @@ -74,6 +74,28 @@ impl VersionParser { Ok(result) } + pub fn new() -> Self { + Self { + inner: SemverVersionParser, + } + } + + pub fn normalize(&self, version: &str, full_version: Option<&str>) -> anyhow::Result<String> { + self.inner.normalize(version, full_version) + } + + pub fn normalize_stability(stability: &str) -> anyhow::Result<String> { + SemverVersionParser::normalize_stability(stability) + } + + pub fn normalize_branch(&self, name: &str) -> anyhow::Result<String> { + self.inner.normalize_branch(name) + } + + pub fn parse_stability(version: &str) -> String { + SemverVersionParser::parse_stability(version) + } + pub fn is_upgrade(normalized_from: &str, normalized_to: &str) -> anyhow::Result<bool> { if normalized_from == normalized_to { return Ok(true); |
