diff options
Diffstat (limited to 'crates/shirabe/src/package')
17 files changed, 1676 insertions, 369 deletions
diff --git a/crates/shirabe/src/package/alias_package.rs b/crates/shirabe/src/package/alias_package.rs index e9b5dbf..5558337 100644 --- a/crates/shirabe/src/package/alias_package.rs +++ b/crates/shirabe/src/package/alias_package.rs @@ -8,6 +8,7 @@ use shirabe_semver::constraint::SimpleConstraint; use crate::package::BasePackage; use crate::package::Link; +use crate::package::PackageHandle; use crate::package::PackageInterface; use crate::package::version::VersionParser; use crate::repository::RepositoryInterface; @@ -34,7 +35,7 @@ pub struct AliasPackage { pub(crate) has_self_version_requires: bool, /// @var BasePackage - pub(crate) alias_of: Box<dyn BasePackage>, + pub(crate) alias_of: PackageHandle, /// @var Link[] pub(crate) requires: IndexMap<String, Link>, /// @var Link[] @@ -53,8 +54,8 @@ impl AliasPackage { /// @param BasePackage $aliasOf The package this package is an alias of /// @param string $version The version the alias must report /// @param string $prettyVersion The alias's non-normalized version - pub fn new(alias_of: Box<dyn BasePackage>, version: String, pretty_version: String) -> Self { - let alias_name = alias_of.get_name().to_string(); + pub fn new(alias_of: PackageHandle, version: String, pretty_version: String) -> Self { + let alias_name = alias_of.get_name(); let stability = VersionParser::parse_stability(&version).to_string(); let dev = stability == "dev"; @@ -130,12 +131,8 @@ impl AliasPackage { this } - pub fn get_alias_of(&self) -> &dyn BasePackage { - self.alias_of.as_ref() - } - - pub fn get_alias_of_mut(&mut self) -> &mut dyn BasePackage { - &mut *self.alias_of + pub fn get_alias_of(&self) -> PackageHandle { + self.alias_of.clone() } /// Stores whether this is an alias created by an aliasing in the requirements of the root package or not @@ -244,11 +241,13 @@ impl PackageInterface for AliasPackage { } fn get_name(&self) -> &str { - self.alias_of.get_name() + // PHP delegates to aliasOf; the local name mirrors aliasOf->getName(), + // so it is returned here to avoid borrowing across the shared handle. + &self.name } fn get_pretty_name(&self) -> &str { - self.alias_of.get_pretty_name() + &self.pretty_name } fn get_names(&self, provides: bool) -> Vec<String> { @@ -306,11 +305,14 @@ impl PackageInterface for AliasPackage { } fn get_type(&self) -> &str { - self.alias_of.get_type() + // Delegates to the shared `aliasOf` handle, whose getters yield owned + // `String`s; a borrow cannot escape the `RefCell`. Use the handle API + // (`AliasPackageHandle::get_alias_of().get_type()`) instead. + todo!("AliasPackage::get_type cannot return &str across the aliasOf handle") } fn get_target_dir(&self) -> Option<&str> { - self.alias_of.get_target_dir() + todo!("AliasPackage::get_target_dir cannot return &str across the aliasOf handle") } fn get_extra(&self) -> IndexMap<String, PhpMixed> { @@ -322,15 +324,15 @@ impl PackageInterface for AliasPackage { } fn get_installation_source(&self) -> Option<&str> { - self.alias_of.get_installation_source() + todo!("AliasPackage::get_installation_source cannot return &str across the aliasOf handle") } fn get_source_type(&self) -> Option<&str> { - self.alias_of.get_source_type() + todo!("AliasPackage::get_source_type cannot return &str across the aliasOf handle") } fn get_source_url(&self) -> Option<&str> { - self.alias_of.get_source_url() + todo!("AliasPackage::get_source_url cannot return &str across the aliasOf handle") } fn get_source_urls(&self) -> Vec<String> { @@ -338,7 +340,7 @@ impl PackageInterface for AliasPackage { } fn get_source_reference(&self) -> Option<&str> { - self.alias_of.get_source_reference() + todo!("AliasPackage::get_source_reference cannot return &str across the aliasOf handle") } fn set_source_reference(&mut self, reference: Option<String>) { @@ -354,11 +356,11 @@ impl PackageInterface for AliasPackage { } fn get_dist_type(&self) -> Option<&str> { - self.alias_of.get_dist_type() + todo!("AliasPackage::get_dist_type cannot return &str across the aliasOf handle") } fn get_dist_url(&self) -> Option<&str> { - self.alias_of.get_dist_url() + todo!("AliasPackage::get_dist_url cannot return &str across the aliasOf handle") } fn get_dist_urls(&self) -> Vec<String> { @@ -366,7 +368,7 @@ impl PackageInterface for AliasPackage { } fn get_dist_reference(&self) -> Option<&str> { - self.alias_of.get_dist_reference() + todo!("AliasPackage::get_dist_reference cannot return &str across the aliasOf handle") } fn set_dist_reference(&mut self, reference: Option<String>) { @@ -374,7 +376,7 @@ impl PackageInterface for AliasPackage { } fn get_dist_sha1_checksum(&self) -> Option<&str> { - self.alias_of.get_dist_sha1_checksum() + todo!("AliasPackage::get_dist_sha1_checksum cannot return &str across the aliasOf handle") } fn set_transport_options(&mut self, options: IndexMap<String, PhpMixed>) { @@ -422,7 +424,7 @@ impl PackageInterface for AliasPackage { } fn get_notification_url(&self) -> Option<&str> { - self.alias_of.get_notification_url() + todo!("AliasPackage::get_notification_url cannot return &str across the aliasOf handle") } fn is_default_branch(&self) -> bool { @@ -442,9 +444,8 @@ 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 - BasePackage::get_full_pretty_version(self.alias_of.as_ref(), truncate, display_mode) - .unwrap_or_default() + self.alias_of + .get_full_pretty_version(truncate, display_mode) } fn get_unique_name(&self) -> String { @@ -460,7 +461,7 @@ impl PackageInterface for AliasPackage { } fn get_repository(&self) -> Option<&dyn RepositoryInterface> { - self.alias_of.get_repository() + todo!("AliasPackage::get_repository cannot return a borrow across the aliasOf handle") } } @@ -500,8 +501,4 @@ impl BasePackage for AliasPackage { fn take_repository(&mut self) -> Option<Box<dyn RepositoryInterface>> { todo!() } - - fn clone_box(&self) -> Box<dyn BasePackage> { - todo!() - } } diff --git a/crates/shirabe/src/package/base_package.rs b/crates/shirabe/src/package/base_package.rs index 75bd22f..64a5919 100644 --- a/crates/shirabe/src/package/base_package.rs +++ b/crates/shirabe/src/package/base_package.rs @@ -89,8 +89,6 @@ pub trait BasePackage: PackageInterface + std::fmt::Display { // TODO(phase-b): wire up a back-reference to the containing repository when needed. } - fn clone_box(&self) -> Box<dyn BasePackage>; - // as_alias_package / as_complete_package_interface inherited from PackageInterface. fn as_alias_package_mut(&mut self) -> Option<&mut crate::package::AliasPackage> { diff --git a/crates/shirabe/src/package/complete_alias_package.rs b/crates/shirabe/src/package/complete_alias_package.rs index 530bd03..6a23bee 100644 --- a/crates/shirabe/src/package/complete_alias_package.rs +++ b/crates/shirabe/src/package/complete_alias_package.rs @@ -1,136 +1,156 @@ //! ref: composer/src/Composer/Package/CompleteAliasPackage.php +use indexmap::IndexMap; +use shirabe_php_shim::PhpMixed; + use crate::package::AliasPackage; -use crate::package::CompletePackage; +use crate::package::CompletePackageHandle; use crate::package::CompletePackageInterface; +use crate::package::PackageHandle; +use crate::package::handle::delegate_package_interface_to_inner; #[derive(Debug)] pub struct CompleteAliasPackage { inner: AliasPackage, // overrides AliasPackage::alias_of with the more specific CompletePackage type - pub(crate) alias_of: CompletePackage, + pub(crate) alias_of: CompletePackageHandle, } impl CompleteAliasPackage { - pub fn new(alias_of: CompletePackage, version: String, pretty_version: String) -> Self { - // 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>. + pub fn new(alias_of: CompletePackageHandle, version: String, pretty_version: String) -> Self { let inner = AliasPackage::new( - todo!("share CompletePackage via Rc"), + PackageHandle::from(alias_of.clone()), version, pretty_version, ); Self { inner, alias_of } } - pub fn get_alias_of(&self) -> &CompletePackage { - &self.alias_of + pub fn get_alias_of(&self) -> CompletePackageHandle { + self.alias_of.clone() + } + + pub fn set_root_package_alias(&mut self, value: bool) { + self.inner.set_root_package_alias(value); + } + + pub fn is_root_package_alias(&self) -> bool { + self.inner.is_root_package_alias() + } + + pub fn has_self_version_requires(&self) -> bool { + self.inner.has_self_version_requires() + } +} + +delegate_package_interface_to_inner!(CompleteAliasPackage, inner); + +impl std::fmt::Display for CompleteAliasPackage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self.inner, f) } +} - pub fn get_scripts(&self) -> indexmap::IndexMap<String, Vec<String>> { +impl CompletePackageInterface for CompleteAliasPackage { + fn get_scripts(&self) -> IndexMap<String, Vec<String>> { self.alias_of.get_scripts() } - pub fn set_scripts(&mut self, scripts: indexmap::IndexMap<String, Vec<String>>) { + fn set_scripts(&mut self, scripts: IndexMap<String, Vec<String>>) { self.alias_of.set_scripts(scripts); } - pub fn get_repositories(&self) -> Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> { + fn get_repositories(&self) -> Vec<IndexMap<String, PhpMixed>> { self.alias_of.get_repositories() } - pub fn set_repositories( - &mut self, - repositories: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>, - ) { + fn set_repositories(&mut self, repositories: Vec<IndexMap<String, PhpMixed>>) { self.alias_of.set_repositories(repositories); } - pub fn get_license(&self) -> Vec<String> { + fn get_license(&self) -> Vec<String> { self.alias_of.get_license() } - pub fn set_license(&mut self, license: Vec<String>) { + fn set_license(&mut self, license: Vec<String>) { self.alias_of.set_license(license); } - pub fn get_keywords(&self) -> Vec<String> { + fn get_keywords(&self) -> Vec<String> { self.alias_of.get_keywords() } - pub fn set_keywords(&mut self, keywords: Vec<String>) { + fn set_keywords(&mut self, keywords: Vec<String>) { self.alias_of.set_keywords(keywords); } - pub fn get_description(&self) -> Option<&str> { - self.alias_of.get_description() + fn get_description(&self) -> Option<&str> { + todo!("CompleteAliasPackage::get_description cannot return &str across the aliasOf handle") } - pub fn set_description(&mut self, description: Option<String>) { - self.alias_of - .set_description(description.unwrap_or_default()); + fn set_description(&mut self, description: String) { + self.alias_of.set_description(description); } - pub fn get_homepage(&self) -> Option<&str> { - self.alias_of.get_homepage() + fn get_homepage(&self) -> Option<&str> { + todo!("CompleteAliasPackage::get_homepage cannot return &str across the aliasOf handle") } - pub fn set_homepage(&mut self, homepage: Option<String>) { - self.alias_of.set_homepage(homepage.unwrap_or_default()); + fn set_homepage(&mut self, homepage: String) { + self.alias_of.set_homepage(homepage); } - pub fn get_authors(&self) -> Vec<indexmap::IndexMap<String, String>> { + fn get_authors(&self) -> Vec<IndexMap<String, String>> { self.alias_of.get_authors() } - pub fn set_authors(&mut self, authors: Vec<indexmap::IndexMap<String, String>>) { + fn set_authors(&mut self, authors: Vec<IndexMap<String, String>>) { self.alias_of.set_authors(authors); } - pub fn get_support(&self) -> indexmap::IndexMap<String, String> { + fn get_support(&self) -> IndexMap<String, String> { self.alias_of.get_support() } - pub fn set_support(&mut self, support: indexmap::IndexMap<String, String>) { + fn set_support(&mut self, support: IndexMap<String, String>) { self.alias_of.set_support(support); } - pub fn get_funding(&self) -> Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> { + fn get_funding(&self) -> Vec<IndexMap<String, PhpMixed>> { self.alias_of.get_funding() } - pub fn set_funding( - &mut self, - funding: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>, - ) { + fn set_funding(&mut self, funding: Vec<IndexMap<String, PhpMixed>>) { self.alias_of.set_funding(funding); } - pub fn is_abandoned(&self) -> bool { + fn is_abandoned(&self) -> bool { self.alias_of.is_abandoned() } - pub fn get_replacement_package(&self) -> Option<&str> { - self.alias_of.get_replacement_package() + fn get_replacement_package(&self) -> Option<&str> { + todo!( + "CompleteAliasPackage::get_replacement_package cannot return &str across the aliasOf handle" + ) } - pub fn set_abandoned(&mut self, abandoned: shirabe_php_shim::PhpMixed) { + fn set_abandoned(&mut self, abandoned: PhpMixed) { self.alias_of.set_abandoned(abandoned); } - pub fn get_archive_name(&self) -> Option<&str> { - self.alias_of.get_archive_name() + fn get_archive_name(&self) -> Option<&str> { + todo!("CompleteAliasPackage::get_archive_name cannot return &str across the aliasOf handle") } - pub fn set_archive_name(&mut self, name: Option<String>) { - self.alias_of.set_archive_name(name.unwrap_or_default()); + fn set_archive_name(&mut self, name: String) { + self.alias_of.set_archive_name(name); } - pub fn get_archive_excludes(&self) -> Vec<String> { + fn get_archive_excludes(&self) -> Vec<String> { self.alias_of.get_archive_excludes() } - pub fn set_archive_excludes(&mut self, excludes: Vec<String>) { + fn set_archive_excludes(&mut self, excludes: Vec<String>) { self.alias_of.set_archive_excludes(excludes); } } diff --git a/crates/shirabe/src/package/handle.rs b/crates/shirabe/src/package/handle.rs new file mode 100644 index 0000000..e1f0570 --- /dev/null +++ b/crates/shirabe/src/package/handle.rs @@ -0,0 +1,1440 @@ +//! Shared handles over the package types. +//! +//! No weak handles are provided: an alias package never aliases another alias package, so the +//! `alias_of` references are acyclic. + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::package::{ + AliasPackage, CompleteAliasPackage, CompletePackage, CompletePackageInterface, Package, + PackageInterface, RootAliasPackage, RootPackage, RootPackageInterface, +}; + +/// Any package type. +#[derive(Debug)] +pub enum AnyPackage { + Package(Package), + CompletePackage(CompletePackage), + RootPackage(RootPackage), + AliasPackage(AliasPackage), + CompleteAliasPackage(CompleteAliasPackage), + RootAliasPackage(RootAliasPackage), +} + +impl AnyPackage { + pub fn as_package_interface(&self) -> &dyn PackageInterface { + match self { + Self::Package(p) => p, + Self::CompletePackage(p) => p, + Self::RootPackage(p) => p, + Self::AliasPackage(p) => p, + Self::CompleteAliasPackage(p) => p, + Self::RootAliasPackage(p) => p, + } + } + + pub fn as_package_interface_mut(&mut self) -> &mut dyn PackageInterface { + match self { + Self::Package(p) => p, + Self::CompletePackage(p) => p, + Self::RootPackage(p) => p, + Self::AliasPackage(p) => p, + Self::CompleteAliasPackage(p) => p, + Self::RootAliasPackage(p) => p, + } + } + + pub fn as_complete_package_interface(&self) -> Option<&dyn CompletePackageInterface> { + match self { + Self::CompletePackage(p) => Some(p), + Self::RootPackage(p) => Some(p), + Self::CompleteAliasPackage(p) => Some(p), + Self::RootAliasPackage(p) => Some(p), + _ => None, + } + } + + pub fn as_complete_package_interface_mut( + &mut self, + ) -> Option<&mut dyn CompletePackageInterface> { + match self { + Self::CompletePackage(p) => Some(p), + Self::RootPackage(p) => Some(p), + Self::CompleteAliasPackage(p) => Some(p), + Self::RootAliasPackage(p) => Some(p), + _ => None, + } + } + + pub fn as_root_package_interface(&self) -> Option<&dyn RootPackageInterface> { + match self { + Self::RootPackage(p) => Some(p), + Self::RootAliasPackage(p) => Some(p), + _ => None, + } + } + + pub fn as_root_package_interface_mut(&mut self) -> Option<&mut dyn RootPackageInterface> { + match self { + Self::RootPackage(p) => Some(p), + Self::RootAliasPackage(p) => Some(p), + _ => None, + } + } + + /// PHP `$p instanceof AliasPackage`. + pub fn is_alias(&self) -> bool { + matches!( + self, + Self::AliasPackage(_) | Self::CompleteAliasPackage(_) | Self::RootAliasPackage(_) + ) + } + + /// PHP `$p instanceof CompletePackageInterface`. + pub fn is_complete(&self) -> bool { + matches!( + self, + Self::CompletePackage(_) + | Self::RootPackage(_) + | Self::CompleteAliasPackage(_) + | Self::RootAliasPackage(_) + ) + } + + /// PHP `$p instanceof RootPackageInterface`. + pub fn is_root(&self) -> bool { + matches!(self, Self::RootPackage(_) | Self::RootAliasPackage(_)) + } + + /// A real (non-alias) package: `Package` / `CompletePackage` / `RootPackage`. + pub fn is_real(&self) -> bool { + matches!( + self, + Self::Package(_) | Self::CompletePackage(_) | Self::RootPackage(_) + ) + } + + /// A real `CompletePackage` or `RootPackage`. + pub fn is_complete_real(&self) -> bool { + matches!(self, Self::CompletePackage(_) | Self::RootPackage(_)) + } + + /// A real `RootPackage`. + pub fn is_root_real(&self) -> bool { + matches!(self, Self::RootPackage(_)) + } + + /// A `CompleteAliasPackage` or `RootAliasPackage`. + pub fn is_complete_alias(&self) -> bool { + matches!( + self, + Self::CompleteAliasPackage(_) | Self::RootAliasPackage(_) + ) + } + + /// A `RootAliasPackage`. + pub fn is_root_alias(&self) -> bool { + matches!(self, Self::RootAliasPackage(_)) + } +} + +macro_rules! delegate_package_interface_to_inner { + ($Type:ty, $field:ident) => { + impl crate::package::PackageInterface for $Type { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn get_name(&self) -> &str { + self.$field.get_name() + } + fn get_pretty_name(&self) -> &str { + self.$field.get_pretty_name() + } + fn get_names(&self, provides: bool) -> Vec<String> { + self.$field.get_names(provides) + } + fn set_id(&mut self, id: i64) { + self.$field.set_id(id); + } + fn get_id(&self) -> i64 { + self.$field.get_id() + } + fn is_dev(&self) -> bool { + self.$field.is_dev() + } + fn get_type(&self) -> &str { + self.$field.get_type() + } + fn get_target_dir(&self) -> Option<&str> { + self.$field.get_target_dir() + } + fn get_extra(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> { + self.$field.get_extra() + } + fn set_installation_source(&mut self, r#type: Option<String>) { + self.$field.set_installation_source(r#type); + } + fn get_installation_source(&self) -> Option<&str> { + self.$field.get_installation_source() + } + fn get_source_type(&self) -> Option<&str> { + self.$field.get_source_type() + } + fn get_source_url(&self) -> Option<&str> { + self.$field.get_source_url() + } + fn get_source_urls(&self) -> Vec<String> { + self.$field.get_source_urls() + } + fn get_source_reference(&self) -> Option<&str> { + self.$field.get_source_reference() + } + fn get_source_mirrors( + &self, + ) -> Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>> { + self.$field.get_source_mirrors() + } + fn set_source_mirrors( + &mut self, + mirrors: Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>>, + ) { + self.$field.set_source_mirrors(mirrors); + } + fn get_dist_type(&self) -> Option<&str> { + self.$field.get_dist_type() + } + fn get_dist_url(&self) -> Option<&str> { + self.$field.get_dist_url() + } + fn get_dist_urls(&self) -> Vec<String> { + self.$field.get_dist_urls() + } + fn get_dist_reference(&self) -> Option<&str> { + self.$field.get_dist_reference() + } + fn get_dist_sha1_checksum(&self) -> Option<&str> { + self.$field.get_dist_sha1_checksum() + } + fn get_dist_mirrors( + &self, + ) -> Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>> { + self.$field.get_dist_mirrors() + } + fn set_dist_mirrors( + &mut self, + mirrors: Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>>, + ) { + self.$field.set_dist_mirrors(mirrors); + } + fn get_version(&self) -> &str { + self.$field.get_version() + } + fn get_pretty_version(&self) -> &str { + self.$field.get_pretty_version() + } + fn get_full_pretty_version(&self, truncate: bool, display_mode: i64) -> String { + self.$field.get_full_pretty_version(truncate, display_mode) + } + fn get_release_date(&self) -> Option<chrono::DateTime<chrono::Utc>> { + self.$field.get_release_date() + } + fn get_stability(&self) -> &str { + self.$field.get_stability() + } + fn get_requires(&self) -> indexmap::IndexMap<String, crate::package::Link> { + self.$field.get_requires() + } + fn get_conflicts(&self) -> indexmap::IndexMap<String, crate::package::Link> { + self.$field.get_conflicts() + } + fn get_provides(&self) -> indexmap::IndexMap<String, crate::package::Link> { + self.$field.get_provides() + } + fn get_replaces(&self) -> indexmap::IndexMap<String, crate::package::Link> { + self.$field.get_replaces() + } + fn get_dev_requires(&self) -> indexmap::IndexMap<String, crate::package::Link> { + self.$field.get_dev_requires() + } + fn get_suggests(&self) -> indexmap::IndexMap<String, String> { + self.$field.get_suggests() + } + fn get_autoload(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> { + self.$field.get_autoload() + } + fn get_dev_autoload(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> { + self.$field.get_dev_autoload() + } + fn get_include_paths(&self) -> Vec<String> { + self.$field.get_include_paths() + } + fn get_php_ext( + &self, + ) -> Option<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> { + self.$field.get_php_ext() + } + fn set_repository( + &mut self, + repository: Box<dyn crate::repository::RepositoryInterface>, + ) -> anyhow::Result<()> { + self.$field.set_repository(repository) + } + fn get_repository(&self) -> Option<&dyn crate::repository::RepositoryInterface> { + self.$field.get_repository() + } + fn get_binaries(&self) -> Vec<String> { + self.$field.get_binaries() + } + fn get_unique_name(&self) -> String { + self.$field.get_unique_name() + } + fn get_notification_url(&self) -> Option<&str> { + self.$field.get_notification_url() + } + fn get_pretty_string(&self) -> String { + self.$field.get_pretty_string() + } + fn is_default_branch(&self) -> bool { + self.$field.is_default_branch() + } + fn get_transport_options( + &self, + ) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> { + self.$field.get_transport_options() + } + fn set_transport_options( + &mut self, + options: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>, + ) { + self.$field.set_transport_options(options); + } + fn set_source_reference(&mut self, reference: Option<String>) { + self.$field.set_source_reference(reference); + } + fn set_dist_url(&mut self, url: Option<String>) { + self.$field.set_dist_url(url); + } + fn set_dist_type(&mut self, r#type: Option<String>) { + self.$field.set_dist_type(r#type); + } + fn set_dist_reference(&mut self, reference: Option<String>) { + self.$field.set_dist_reference(reference); + } + fn set_source_dist_references(&mut self, reference: &str) { + self.$field.set_source_dist_references(reference); + } + } + }; +} +pub(crate) use delegate_package_interface_to_inner; + +macro_rules! impl_package_interface_handle { + ($Handle:ty) => { + impl $Handle { + pub fn get_name(&self) -> String { + self.0 + .borrow() + .as_package_interface() + .get_name() + .to_string() + } + + pub fn get_pretty_name(&self) -> String { + self.0 + .borrow() + .as_package_interface() + .get_pretty_name() + .to_string() + } + + pub fn get_names(&self, provides: bool) -> Vec<String> { + self.0.borrow().as_package_interface().get_names(provides) + } + + pub fn set_id(&self, id: i64) { + self.0.borrow_mut().as_package_interface_mut().set_id(id); + } + + pub fn get_id(&self) -> i64 { + self.0.borrow().as_package_interface().get_id() + } + + /// PHP `BasePackage::$id` accessor; alias of [`get_id`](Self::get_id). + pub fn id(&self) -> i64 { + self.0.borrow().as_package_interface().get_id() + } + + pub fn is_dev(&self) -> bool { + self.0.borrow().as_package_interface().is_dev() + } + + pub fn get_type(&self) -> String { + self.0 + .borrow() + .as_package_interface() + .get_type() + .to_string() + } + + pub fn get_target_dir(&self) -> Option<String> { + self.0 + .borrow() + .as_package_interface() + .get_target_dir() + .map(str::to_string) + } + + pub fn get_extra(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> { + self.0.borrow().as_package_interface().get_extra() + } + + pub fn set_installation_source(&self, r#type: Option<String>) { + self.0 + .borrow_mut() + .as_package_interface_mut() + .set_installation_source(r#type); + } + + pub fn get_installation_source(&self) -> Option<String> { + self.0 + .borrow() + .as_package_interface() + .get_installation_source() + .map(str::to_string) + } + + pub fn get_source_type(&self) -> Option<String> { + self.0 + .borrow() + .as_package_interface() + .get_source_type() + .map(str::to_string) + } + + pub fn get_source_url(&self) -> Option<String> { + self.0 + .borrow() + .as_package_interface() + .get_source_url() + .map(str::to_string) + } + + pub fn get_source_urls(&self) -> Vec<String> { + self.0.borrow().as_package_interface().get_source_urls() + } + + pub fn get_source_reference(&self) -> Option<String> { + self.0 + .borrow() + .as_package_interface() + .get_source_reference() + .map(str::to_string) + } + + pub fn get_source_mirrors( + &self, + ) -> Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>> { + self.0.borrow().as_package_interface().get_source_mirrors() + } + + pub fn set_source_mirrors( + &self, + mirrors: Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>>, + ) { + self.0 + .borrow_mut() + .as_package_interface_mut() + .set_source_mirrors(mirrors); + } + + pub fn get_dist_type(&self) -> Option<String> { + self.0 + .borrow() + .as_package_interface() + .get_dist_type() + .map(str::to_string) + } + + pub fn get_dist_url(&self) -> Option<String> { + self.0 + .borrow() + .as_package_interface() + .get_dist_url() + .map(str::to_string) + } + + pub fn get_dist_urls(&self) -> Vec<String> { + self.0.borrow().as_package_interface().get_dist_urls() + } + + pub fn get_dist_reference(&self) -> Option<String> { + self.0 + .borrow() + .as_package_interface() + .get_dist_reference() + .map(str::to_string) + } + + pub fn get_dist_sha1_checksum(&self) -> Option<String> { + self.0 + .borrow() + .as_package_interface() + .get_dist_sha1_checksum() + .map(str::to_string) + } + + pub fn get_dist_mirrors( + &self, + ) -> Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>> { + self.0.borrow().as_package_interface().get_dist_mirrors() + } + + pub fn set_dist_mirrors( + &self, + mirrors: Option<Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>>, + ) { + self.0 + .borrow_mut() + .as_package_interface_mut() + .set_dist_mirrors(mirrors); + } + + pub fn get_version(&self) -> String { + self.0 + .borrow() + .as_package_interface() + .get_version() + .to_string() + } + + pub fn get_pretty_version(&self) -> String { + self.0 + .borrow() + .as_package_interface() + .get_pretty_version() + .to_string() + } + + pub fn get_full_pretty_version(&self, truncate: bool, display_mode: i64) -> String { + self.0 + .borrow() + .as_package_interface() + .get_full_pretty_version(truncate, display_mode) + } + + pub fn get_release_date(&self) -> Option<chrono::DateTime<chrono::Utc>> { + self.0.borrow().as_package_interface().get_release_date() + } + + pub fn get_stability(&self) -> String { + self.0 + .borrow() + .as_package_interface() + .get_stability() + .to_string() + } + + pub fn get_requires(&self) -> indexmap::IndexMap<String, crate::package::Link> { + self.0.borrow().as_package_interface().get_requires() + } + + pub fn get_conflicts(&self) -> indexmap::IndexMap<String, crate::package::Link> { + self.0.borrow().as_package_interface().get_conflicts() + } + + pub fn get_provides(&self) -> indexmap::IndexMap<String, crate::package::Link> { + self.0.borrow().as_package_interface().get_provides() + } + + pub fn get_replaces(&self) -> indexmap::IndexMap<String, crate::package::Link> { + self.0.borrow().as_package_interface().get_replaces() + } + + pub fn get_dev_requires(&self) -> indexmap::IndexMap<String, crate::package::Link> { + self.0.borrow().as_package_interface().get_dev_requires() + } + + pub fn get_suggests(&self) -> indexmap::IndexMap<String, String> { + self.0.borrow().as_package_interface().get_suggests() + } + + pub fn get_links_for_type( + &self, + link_type: &str, + ) -> indexmap::IndexMap<String, crate::package::Link> { + self.0 + .borrow() + .as_package_interface() + .get_links_for_type(link_type) + } + + pub fn get_autoload(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> { + self.0.borrow().as_package_interface().get_autoload() + } + + pub fn get_dev_autoload( + &self, + ) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> { + self.0.borrow().as_package_interface().get_dev_autoload() + } + + pub fn get_include_paths(&self) -> Vec<String> { + self.0.borrow().as_package_interface().get_include_paths() + } + + pub fn get_php_ext( + &self, + ) -> Option<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> { + self.0.borrow().as_package_interface().get_php_ext() + } + + pub fn set_repository( + &self, + repository: Box<dyn crate::repository::RepositoryInterface>, + ) -> anyhow::Result<()> { + self.0 + .borrow_mut() + .as_package_interface_mut() + .set_repository(repository) + } + + pub fn get_binaries(&self) -> Vec<String> { + self.0.borrow().as_package_interface().get_binaries() + } + + pub fn get_unique_name(&self) -> String { + self.0.borrow().as_package_interface().get_unique_name() + } + + pub fn get_notification_url(&self) -> Option<String> { + self.0 + .borrow() + .as_package_interface() + .get_notification_url() + .map(str::to_string) + } + + pub fn get_pretty_string(&self) -> String { + self.0.borrow().as_package_interface().get_pretty_string() + } + + pub fn is_default_branch(&self) -> bool { + self.0.borrow().as_package_interface().is_default_branch() + } + + pub fn get_transport_options( + &self, + ) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> { + self.0 + .borrow() + .as_package_interface() + .get_transport_options() + } + + pub fn set_transport_options( + &self, + options: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>, + ) { + self.0 + .borrow_mut() + .as_package_interface_mut() + .set_transport_options(options); + } + + pub fn set_source_reference(&self, reference: Option<String>) { + self.0 + .borrow_mut() + .as_package_interface_mut() + .set_source_reference(reference); + } + + pub fn set_dist_url(&self, url: Option<String>) { + self.0 + .borrow_mut() + .as_package_interface_mut() + .set_dist_url(url); + } + + pub fn set_dist_type(&self, r#type: Option<String>) { + self.0 + .borrow_mut() + .as_package_interface_mut() + .set_dist_type(r#type); + } + + pub fn set_dist_reference(&self, reference: Option<String>) { + self.0 + .borrow_mut() + .as_package_interface_mut() + .set_dist_reference(reference); + } + + pub fn set_source_dist_references(&self, reference: &str) { + self.0 + .borrow_mut() + .as_package_interface_mut() + .set_source_dist_references(reference); + } + } + }; +} + +macro_rules! impl_complete_package_interface_handle { + ($Handle:ty) => { + impl $Handle { + pub fn get_scripts(&self) -> indexmap::IndexMap<String, Vec<String>> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_scripts() + } + + pub fn set_scripts(&self, scripts: indexmap::IndexMap<String, Vec<String>>) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_scripts(scripts); + } + + pub fn get_repositories( + &self, + ) -> Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_repositories() + } + + pub fn set_repositories( + &self, + repositories: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>, + ) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_repositories(repositories); + } + + pub fn get_license(&self) -> Vec<String> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_license() + } + + pub fn set_license(&self, license: Vec<String>) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_license(license); + } + + pub fn get_keywords(&self) -> Vec<String> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_keywords() + } + + pub fn set_keywords(&self, keywords: Vec<String>) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_keywords(keywords); + } + + pub fn get_description(&self) -> Option<String> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_description() + .map(str::to_string) + } + + pub fn set_description(&self, description: String) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_description(description); + } + + pub fn get_homepage(&self) -> Option<String> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_homepage() + .map(str::to_string) + } + + pub fn set_homepage(&self, homepage: String) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_homepage(homepage); + } + + pub fn get_authors(&self) -> Vec<indexmap::IndexMap<String, String>> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_authors() + } + + pub fn set_authors(&self, authors: Vec<indexmap::IndexMap<String, String>>) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_authors(authors); + } + + pub fn get_support(&self) -> indexmap::IndexMap<String, String> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_support() + } + + pub fn set_support(&self, support: indexmap::IndexMap<String, String>) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_support(support); + } + + pub fn get_funding( + &self, + ) -> Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_funding() + } + + pub fn set_funding( + &self, + funding: Vec<indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>>, + ) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_funding(funding); + } + + pub fn is_abandoned(&self) -> bool { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .is_abandoned() + } + + pub fn get_replacement_package(&self) -> Option<String> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_replacement_package() + .map(str::to_string) + } + + pub fn set_abandoned(&self, abandoned: shirabe_php_shim::PhpMixed) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_abandoned(abandoned); + } + + pub fn get_archive_name(&self) -> Option<String> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_archive_name() + .map(str::to_string) + } + + pub fn set_archive_name(&self, name: String) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_archive_name(name); + } + + pub fn get_archive_excludes(&self) -> Vec<String> { + self.0 + .borrow() + .as_complete_package_interface() + .expect("CompletePackage handle invariant") + .get_archive_excludes() + } + + pub fn set_archive_excludes(&self, excludes: Vec<String>) { + self.0 + .borrow_mut() + .as_complete_package_interface_mut() + .expect("CompletePackage handle invariant") + .set_archive_excludes(excludes); + } + } + }; +} + +macro_rules! impl_root_package_interface_handle { + ($Handle:ty) => { + impl $Handle { + pub fn get_aliases(&self) -> Vec<indexmap::IndexMap<String, String>> { + self.0 + .borrow() + .as_root_package_interface() + .expect("RootPackage handle invariant") + .get_aliases() + .to_vec() + } + + pub fn get_minimum_stability(&self) -> String { + self.0 + .borrow() + .as_root_package_interface() + .expect("RootPackage handle invariant") + .get_minimum_stability() + .to_string() + } + + pub fn get_stability_flags(&self) -> indexmap::IndexMap<String, i64> { + self.0 + .borrow() + .as_root_package_interface() + .expect("RootPackage handle invariant") + .get_stability_flags() + .clone() + } + + pub fn get_references(&self) -> indexmap::IndexMap<String, String> { + self.0 + .borrow() + .as_root_package_interface() + .expect("RootPackage handle invariant") + .get_references() + .clone() + } + + pub fn get_prefer_stable(&self) -> bool { + self.0 + .borrow() + .as_root_package_interface() + .expect("RootPackage handle invariant") + .get_prefer_stable() + } + + pub fn get_config(&self) -> indexmap::IndexMap<String, shirabe_php_shim::PhpMixed> { + self.0 + .borrow() + .as_root_package_interface() + .expect("RootPackage handle invariant") + .get_config() + .clone() + } + + pub fn set_requires(&self, requires: Vec<crate::package::Link>) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_requires(requires); + } + + pub fn set_dev_requires(&self, dev_requires: Vec<crate::package::Link>) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_dev_requires(dev_requires); + } + + pub fn set_conflicts(&self, conflicts: Vec<crate::package::Link>) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_conflicts(conflicts); + } + + pub fn set_provides(&self, provides: Vec<crate::package::Link>) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_provides(provides); + } + + pub fn set_replaces(&self, replaces: Vec<crate::package::Link>) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_replaces(replaces); + } + + pub fn set_autoload( + &self, + autoload: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>, + ) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_autoload(autoload); + } + + pub fn set_dev_autoload( + &self, + dev_autoload: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>, + ) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_dev_autoload(dev_autoload); + } + + pub fn set_stability_flags(&self, stability_flags: indexmap::IndexMap<String, i64>) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_stability_flags(stability_flags); + } + + pub fn set_minimum_stability(&self, minimum_stability: String) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_minimum_stability(minimum_stability); + } + + pub fn set_prefer_stable(&self, prefer_stable: bool) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_prefer_stable(prefer_stable); + } + + pub fn set_config( + &self, + config: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>, + ) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_config(config); + } + + pub fn set_references(&self, references: indexmap::IndexMap<String, String>) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_references(references); + } + + pub fn set_aliases(&self, aliases: Vec<indexmap::IndexMap<String, String>>) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_aliases(aliases); + } + + pub fn set_suggests(&self, suggests: indexmap::IndexMap<String, String>) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_suggests(suggests); + } + + pub fn set_extra(&self, extra: indexmap::IndexMap<String, shirabe_php_shim::PhpMixed>) { + self.0 + .borrow_mut() + .as_root_package_interface_mut() + .expect("RootPackage handle invariant") + .set_extra(extra); + } + } + }; +} + +macro_rules! impl_handle_common { + ($Handle:ty) => { + impl $Handle { + pub fn as_rc(&self) -> &std::rc::Rc<std::cell::RefCell<AnyPackage>> { + &self.0 + } + + pub fn from_rc_unchecked(rc: std::rc::Rc<std::cell::RefCell<AnyPackage>>) -> Self { + Self(rc) + } + + /// Stable identity usable as a map key (PHP `spl_object_hash`). + pub fn ptr_id(&self) -> usize { + std::rc::Rc::as_ptr(&self.0) as *const () as usize + } + + /// PHP `===` (reference identity). + pub fn ptr_eq(&self, other: &Self) -> bool { + std::rc::Rc::ptr_eq(&self.0, &other.0) + } + } + + impl PartialEq for $Handle { + fn eq(&self, other: &Self) -> bool { + std::rc::Rc::ptr_eq(&self.0, &other.0) + } + } + + impl Eq for $Handle {} + + impl std::hash::Hash for $Handle { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.ptr_id().hash(state); + } + } + + impl std::fmt::Display for $Handle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.0.borrow().as_package_interface(), f) + } + } + }; +} + +macro_rules! impl_handle_upcast { + ($Narrow:ty => $Wide:ty) => { + impl From<$Narrow> for $Wide { + fn from(h: $Narrow) -> Self { + Self(h.0) + } + } + }; +} + +/// Shared reference to any package. Corresponds to PHP `PackageInterface`. +#[derive(Debug, Clone)] +pub struct PackageInterfaceHandle(Rc<RefCell<AnyPackage>>); + +/// Shared reference to any package. Corresponds to PHP `BasePackage`. +/// It is exactly the same as `PackageInterface` in Shirabe. It is only for mirroing PHP type +/// annotations. +pub type BasePackageHandle = PackageInterfaceHandle; + +/// Shared reference to a complete package. Corresponds to PHP `CompletePackageInterface`. +#[derive(Debug, Clone)] +pub struct CompletePackageInterfaceHandle(Rc<RefCell<AnyPackage>>); + +/// Shared reference to a root package. Corresponds to PHP `RootPackageInterface`. +#[derive(Debug, Clone)] +pub struct RootPackageInterfaceHandle(Rc<RefCell<AnyPackage>>); + +/// Shared reference to a real (non-alias) package. Corresponds to PHP `Package`. +#[derive(Debug, Clone)] +pub struct PackageHandle(Rc<RefCell<AnyPackage>>); + +/// Shared reference to a real complete package. Corresponds to PHP `CompletePackage`. +#[derive(Debug, Clone)] +pub struct CompletePackageHandle(Rc<RefCell<AnyPackage>>); + +/// Shared reference to a real root package. Corresponds to PHP `RootPackage`. +#[derive(Debug, Clone)] +pub struct RootPackageHandle(Rc<RefCell<AnyPackage>>); + +/// Shared reference to an alias package. Corresponds to PHP `AliasPackage`. +#[derive(Debug, Clone)] +pub struct AliasPackageHandle(Rc<RefCell<AnyPackage>>); + +/// Shared reference to a complete alias package. Corresponds to PHP `CompleteAliasPackage`. +#[derive(Debug, Clone)] +pub struct CompleteAliasPackageHandle(Rc<RefCell<AnyPackage>>); + +/// Shared reference to a root alias package. Corresponds to PHP `RootAliasPackage`. +#[derive(Debug, Clone)] +pub struct RootAliasPackageHandle(Rc<RefCell<AnyPackage>>); + +impl_handle_common!(PackageInterfaceHandle); +impl_handle_common!(CompletePackageInterfaceHandle); +impl_handle_common!(RootPackageInterfaceHandle); +impl_handle_common!(PackageHandle); +impl_handle_common!(CompletePackageHandle); +impl_handle_common!(RootPackageHandle); +impl_handle_common!(AliasPackageHandle); +impl_handle_common!(CompleteAliasPackageHandle); +impl_handle_common!(RootAliasPackageHandle); + +impl_package_interface_handle!(PackageInterfaceHandle); +impl_package_interface_handle!(CompletePackageInterfaceHandle); +impl_package_interface_handle!(RootPackageInterfaceHandle); +impl_package_interface_handle!(PackageHandle); +impl_package_interface_handle!(CompletePackageHandle); +impl_package_interface_handle!(RootPackageHandle); +impl_package_interface_handle!(AliasPackageHandle); +impl_package_interface_handle!(CompleteAliasPackageHandle); +impl_package_interface_handle!(RootAliasPackageHandle); + +impl_complete_package_interface_handle!(CompletePackageInterfaceHandle); +impl_complete_package_interface_handle!(RootPackageInterfaceHandle); +impl_complete_package_interface_handle!(CompletePackageHandle); +impl_complete_package_interface_handle!(RootPackageHandle); +impl_complete_package_interface_handle!(CompleteAliasPackageHandle); +impl_complete_package_interface_handle!(RootAliasPackageHandle); + +impl_root_package_interface_handle!(RootPackageInterfaceHandle); +impl_root_package_interface_handle!(RootPackageHandle); +impl_root_package_interface_handle!(RootAliasPackageHandle); + +impl_handle_upcast!(CompletePackageInterfaceHandle => PackageInterfaceHandle); + +impl_handle_upcast!(RootPackageInterfaceHandle => CompletePackageInterfaceHandle); +impl_handle_upcast!(RootPackageInterfaceHandle => PackageInterfaceHandle); + +impl_handle_upcast!(PackageHandle => PackageInterfaceHandle); + +impl_handle_upcast!(CompletePackageHandle => PackageHandle); +impl_handle_upcast!(CompletePackageHandle => CompletePackageInterfaceHandle); +impl_handle_upcast!(CompletePackageHandle => PackageInterfaceHandle); + +impl_handle_upcast!(RootPackageHandle => CompletePackageHandle); +impl_handle_upcast!(RootPackageHandle => PackageHandle); +impl_handle_upcast!(RootPackageHandle => RootPackageInterfaceHandle); +impl_handle_upcast!(RootPackageHandle => CompletePackageInterfaceHandle); +impl_handle_upcast!(RootPackageHandle => PackageInterfaceHandle); + +impl_handle_upcast!(AliasPackageHandle => PackageInterfaceHandle); + +impl_handle_upcast!(CompleteAliasPackageHandle => AliasPackageHandle); +impl_handle_upcast!(CompleteAliasPackageHandle => CompletePackageInterfaceHandle); +impl_handle_upcast!(CompleteAliasPackageHandle => PackageInterfaceHandle); + +impl_handle_upcast!(RootAliasPackageHandle => CompleteAliasPackageHandle); +impl_handle_upcast!(RootAliasPackageHandle => AliasPackageHandle); +impl_handle_upcast!(RootAliasPackageHandle => RootPackageInterfaceHandle); +impl_handle_upcast!(RootAliasPackageHandle => CompletePackageInterfaceHandle); +impl_handle_upcast!(RootAliasPackageHandle => PackageInterfaceHandle); + +macro_rules! impl_handle_downcasts { + ($Handle:ty) => { + impl $Handle { + /// PHP `$p instanceof AliasPackage`. + pub fn as_alias(&self) -> Option<AliasPackageHandle> { + self.0 + .borrow() + .is_alias() + .then(|| AliasPackageHandle(self.0.clone())) + } + + /// PHP `$p instanceof CompletePackageInterface`. + pub fn as_complete(&self) -> Option<CompletePackageInterfaceHandle> { + self.0 + .borrow() + .is_complete() + .then(|| CompletePackageInterfaceHandle(self.0.clone())) + } + + /// PHP `$p instanceof RootPackageInterface`. + pub fn as_root(&self) -> Option<RootPackageInterfaceHandle> { + self.0 + .borrow() + .is_root() + .then(|| RootPackageInterfaceHandle(self.0.clone())) + } + + /// PHP `$p instanceof Package` (real, non-alias). + pub fn as_package(&self) -> Option<PackageHandle> { + self.0 + .borrow() + .is_real() + .then(|| PackageHandle(self.0.clone())) + } + + /// PHP `$p instanceof CompletePackage` (real). + pub fn as_complete_package(&self) -> Option<CompletePackageHandle> { + self.0 + .borrow() + .is_complete_real() + .then(|| CompletePackageHandle(self.0.clone())) + } + + /// PHP `$p instanceof RootPackage` (real). + pub fn as_root_package(&self) -> Option<RootPackageHandle> { + self.0 + .borrow() + .is_root_real() + .then(|| RootPackageHandle(self.0.clone())) + } + + /// PHP `$p instanceof CompleteAliasPackage`. + pub fn as_complete_alias_package(&self) -> Option<CompleteAliasPackageHandle> { + self.0 + .borrow() + .is_complete_alias() + .then(|| CompleteAliasPackageHandle(self.0.clone())) + } + + /// PHP `$p instanceof RootAliasPackage`. + pub fn as_root_alias_package(&self) -> Option<RootAliasPackageHandle> { + self.0 + .borrow() + .is_root_alias() + .then(|| RootAliasPackageHandle(self.0.clone())) + } + + pub fn is_alias(&self) -> bool { + self.0.borrow().is_alias() + } + } + }; +} + +impl_handle_downcasts!(PackageInterfaceHandle); + +impl PackageHandle { + pub fn from_package(package: Package) -> Self { + Self(Rc::new(RefCell::new(AnyPackage::Package(package)))) + } + + pub fn new(name: String, version: String, pretty_version: String) -> Self { + Self::from_package(Package::new(name, version, pretty_version)) + } +} + +impl CompletePackageHandle { + pub fn from_complete_package(package: CompletePackage) -> Self { + Self(Rc::new(RefCell::new(AnyPackage::CompletePackage(package)))) + } + + pub fn new(name: String, version: String, pretty_version: String) -> Self { + Self::from_complete_package(CompletePackage::new(name, version, pretty_version)) + } +} + +impl RootPackageHandle { + pub fn from_root_package(package: RootPackage) -> Self { + Self(Rc::new(RefCell::new(AnyPackage::RootPackage(package)))) + } + + pub fn new(name: String, version: String, pretty_version: String) -> Self { + Self::from_root_package(RootPackage::new(name, version, pretty_version)) + } +} + +impl AliasPackageHandle { + pub fn from_alias_package(package: AliasPackage) -> Self { + Self(Rc::new(RefCell::new(AnyPackage::AliasPackage(package)))) + } + + pub fn new(alias_of: PackageHandle, version: String, pretty_version: String) -> Self { + Self::from_alias_package(AliasPackage::new(alias_of, version, pretty_version)) + } + + /// PHP `getAliasOf()`. The aliased package is always real. + pub fn get_alias_of(&self) -> PackageHandle { + match &*self.0.borrow() { + AnyPackage::AliasPackage(p) => p.alias_of.clone(), + AnyPackage::CompleteAliasPackage(p) => PackageHandle::from(p.alias_of.clone()), + AnyPackage::RootAliasPackage(p) => PackageHandle::from(p.alias_of.clone()), + _ => unreachable!("AliasPackageHandle invariant violated"), + } + } + + pub fn set_root_package_alias(&self, value: bool) { + match &mut *self.0.borrow_mut() { + AnyPackage::AliasPackage(p) => p.set_root_package_alias(value), + AnyPackage::CompleteAliasPackage(p) => p.set_root_package_alias(value), + AnyPackage::RootAliasPackage(p) => p.set_root_package_alias(value), + _ => unreachable!("AliasPackageHandle invariant violated"), + } + } + + pub fn is_root_package_alias(&self) -> bool { + match &*self.0.borrow() { + AnyPackage::AliasPackage(p) => p.is_root_package_alias(), + AnyPackage::CompleteAliasPackage(p) => p.is_root_package_alias(), + AnyPackage::RootAliasPackage(p) => p.is_root_package_alias(), + _ => unreachable!("AliasPackageHandle invariant violated"), + } + } + + pub fn has_self_version_requires(&self) -> bool { + match &*self.0.borrow() { + AnyPackage::AliasPackage(p) => p.has_self_version_requires(), + AnyPackage::CompleteAliasPackage(p) => p.has_self_version_requires(), + AnyPackage::RootAliasPackage(p) => p.has_self_version_requires(), + _ => unreachable!("AliasPackageHandle invariant violated"), + } + } +} + +impl CompleteAliasPackageHandle { + pub fn from_complete_alias_package(package: CompleteAliasPackage) -> Self { + Self(Rc::new(RefCell::new(AnyPackage::CompleteAliasPackage( + package, + )))) + } + + pub fn new(alias_of: CompletePackageHandle, version: String, pretty_version: String) -> Self { + Self::from_complete_alias_package(CompleteAliasPackage::new( + alias_of, + version, + pretty_version, + )) + } + + /// PHP `getAliasOf()` narrowed to `CompletePackage`. + pub fn get_alias_of(&self) -> CompletePackageHandle { + match &*self.0.borrow() { + AnyPackage::CompleteAliasPackage(p) => p.alias_of.clone(), + AnyPackage::RootAliasPackage(p) => CompletePackageHandle::from(p.alias_of.clone()), + _ => unreachable!("CompleteAliasPackageHandle invariant violated"), + } + } +} + +impl RootAliasPackageHandle { + pub fn from_root_alias_package(package: RootAliasPackage) -> Self { + Self(Rc::new(RefCell::new(AnyPackage::RootAliasPackage(package)))) + } + + pub fn new(alias_of: RootPackageHandle, version: String, pretty_version: String) -> Self { + Self::from_root_alias_package(RootAliasPackage::new(alias_of, version, pretty_version)) + } + + /// PHP `getAliasOf()` narrowed to `RootPackage`. + pub fn get_alias_of(&self) -> RootPackageHandle { + match &*self.0.borrow() { + AnyPackage::RootAliasPackage(p) => p.alias_of.clone(), + _ => unreachable!("RootAliasPackageHandle invariant violated"), + } + } +} diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs index 87d6e35..e1cc60b 100644 --- a/crates/shirabe/src/package/loader/array_loader.rs +++ b/crates/shirabe/src/package/loader/array_loader.rs @@ -12,11 +12,14 @@ use shirabe_php_shim::{ use crate::package::CompleteAliasPackage; use crate::package::CompletePackage; +use crate::package::CompletePackageHandle; use crate::package::CompletePackageInterface; use crate::package::Link; use crate::package::PackageInterface; +use crate::package::PackageInterfaceHandle; use crate::package::RootAliasPackage; use crate::package::RootPackage; +use crate::package::RootPackageHandle; use crate::package::loader::LoaderInterface; use crate::package::version::VersionParser; use crate::package::{BasePackage, SUPPORTED_LINK_TYPES}; @@ -51,7 +54,7 @@ impl LoaderInterface for ArrayLoader { &self, mut config: IndexMap<String, PhpMixed>, class: Option<String>, - ) -> Result<Box<dyn BasePackage>> { + ) -> Result<PackageInterfaceHandle> { let class = class.unwrap_or_else(|| "Composer\\Package\\CompletePackage".to_string()); if class != "Composer\\Package\\CompletePackage" @@ -104,8 +107,8 @@ impl ArrayLoader { pub fn load_packages( &self, versions: Vec<IndexMap<String, PhpMixed>>, - ) -> Result<Vec<Box<dyn BasePackage>>> { - let mut packages: Vec<Box<dyn BasePackage>> = vec![]; + ) -> Result<Vec<PackageInterfaceHandle>> { + let mut packages: Vec<PackageInterfaceHandle> = vec![]; let mut link_cache: IndexMap< String, IndexMap<String, IndexMap<String, IndexMap<String, (String, Link)>>>, @@ -226,7 +229,7 @@ impl ArrayLoader { &self, mut package: Box<CompletePackage>, config: &mut IndexMap<String, PhpMixed>, - ) -> Result<Box<dyn BasePackage>> { + ) -> Result<PackageInterfaceHandle> { // PHP: if (!$package instanceof CompletePackage) — true by construction in Rust // (create_object always returns Box<CompletePackage>); kept as a no-op for parity. let _ = LogicException { @@ -593,12 +596,20 @@ impl ArrayLoader { // TODO(phase-b): `$package instanceof RootPackage` downcast from CompletePackage let package_as_root: Option<RootPackage> = None; if let Some(root) = package_as_root { - let _ = RootAliasPackage::new(root, alias_normalized, pretty_alias); + let _ = RootAliasPackage::new( + RootPackageHandle::from_root_package(root), + alias_normalized, + pretty_alias, + ); // TODO(phase-b): return Box<RootAliasPackage> wrapped as Box<BasePackage> todo!("phase-b: return RootAliasPackage as Box<BasePackage>") } - let _ = CompleteAliasPackage::new(*package, alias_normalized, pretty_alias); + let _ = CompleteAliasPackage::new( + CompletePackageHandle::from_complete_package(*package), + alias_normalized, + pretty_alias, + ); // TODO(phase-b): return Box<CompleteAliasPackage> wrapped as Box<BasePackage> todo!("phase-b: return CompleteAliasPackage as Box<BasePackage>") } diff --git a/crates/shirabe/src/package/loader/json_loader.rs b/crates/shirabe/src/package/loader/json_loader.rs index e0589bb..5f8cc4f 100644 --- a/crates/shirabe/src/package/loader/json_loader.rs +++ b/crates/shirabe/src/package/loader/json_loader.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/Package/Loader/JsonLoader.php use crate::json::JsonFile; -use crate::package::BasePackage; +use crate::package::PackageInterfaceHandle; use crate::package::loader::LoaderInterface; use anyhow::Result; use std::path::Path; @@ -20,7 +20,7 @@ impl JsonLoader { Self { loader } } - pub fn load(&self, json: JsonLoaderInput) -> Result<Box<dyn BasePackage>> { + pub fn load(&self, json: JsonLoaderInput) -> Result<PackageInterfaceHandle> { let config = match json { JsonLoaderInput::File(mut json_file) => json_file.read()?, JsonLoaderInput::String(ref s) if Path::new(s).exists() => { diff --git a/crates/shirabe/src/package/loader/loader_interface.rs b/crates/shirabe/src/package/loader/loader_interface.rs index d10b7be..586905f 100644 --- a/crates/shirabe/src/package/loader/loader_interface.rs +++ b/crates/shirabe/src/package/loader/loader_interface.rs @@ -1,6 +1,6 @@ //! ref: composer/src/Composer/Package/Loader/LoaderInterface.php -use crate::package::BasePackage; +use crate::package::PackageInterfaceHandle; use indexmap::IndexMap; use shirabe_php_shim::PhpMixed; @@ -9,5 +9,5 @@ pub trait LoaderInterface: std::fmt::Debug { &self, config: IndexMap<String, PhpMixed>, class: Option<String>, - ) -> anyhow::Result<Box<dyn BasePackage>>; + ) -> anyhow::Result<PackageInterfaceHandle>; } diff --git a/crates/shirabe/src/package/loader/root_package_loader.rs b/crates/shirabe/src/package/loader/root_package_loader.rs index 3cdaaa7..e235e31 100644 --- a/crates/shirabe/src/package/loader/root_package_loader.rs +++ b/crates/shirabe/src/package/loader/root_package_loader.rs @@ -66,7 +66,7 @@ impl RootPackageLoader { config: IndexMap<String, Box<shirabe_php_shim::PhpMixed>>, class: &str, cwd: Option<&str>, - ) -> anyhow::Result<Box<dyn PackageInterface>> { + ) -> anyhow::Result<crate::package::PackageInterfaceHandle> { if class != "Composer\\Package\\RootPackage" { shirabe_php_shim::trigger_error( "The $class arg is deprecated, please reach out to Composer maintainers ASAP if you still need this.", @@ -193,11 +193,14 @@ impl RootPackageLoader { Some("Composer\\Package\\RootPackage".to_string()), )?; - // TODO(phase-b): as_any_mut is not available on BasePackage; downcast via Any is not - // possible without it. Skipping real downcast and using todo!() placeholder. + // TODO(phase-c): mutating the loaded RootPackage through a PackageInterfaceHandle + // requires going through as_root_package() + a RefCell borrow; the inherent + // RootPackage mutators used below are not yet reachable that way. let real_package: &mut RootPackage = { let _ = &mut package; - todo!("downcast Box<dyn BasePackage> to &mut RootPackage requires as_any_mut on trait") + todo!( + "mutate RootPackage through PackageInterfaceHandle (as_root_package + borrow_mut)" + ) }; if auto_versioned { diff --git a/crates/shirabe/src/package/loader/validating_array_loader.rs b/crates/shirabe/src/package/loader/validating_array_loader.rs index a2dd5a7..fbaa0f8 100644 --- a/crates/shirabe/src/package/loader/validating_array_loader.rs +++ b/crates/shirabe/src/package/loader/validating_array_loader.rs @@ -66,7 +66,7 @@ impl ValidatingArrayLoader { &mut self, config: IndexMap<String, Box<PhpMixed>>, class: &str, - ) -> anyhow::Result<Box<dyn BasePackage>> { + ) -> anyhow::Result<crate::package::PackageInterfaceHandle> { self.errors = Vec::new(); self.warnings = Vec::new(); self.config = config.clone(); diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs index 9522e55..bdeb391 100644 --- a/crates/shirabe/src/package/locker.rs +++ b/crates/shirabe/src/package/locker.rs @@ -15,11 +15,11 @@ use shirabe_php_shim::{ use crate::installer::InstallationManager; use crate::io::IOInterface; use crate::json::JsonFile; -use crate::package::AliasPackage; -use crate::package::BasePackage; +use crate::package::BasePackageHandle; use crate::package::CompleteAliasPackage; use crate::package::Link; use crate::package::PackageInterface; +use crate::package::PackageInterfaceHandle; use crate::package::RootPackageInterface; use crate::package::dumper::ArrayDumper; use crate::package::loader::ArrayLoader; @@ -219,15 +219,15 @@ impl Locker { false }; if has_name { - let mut package_by_name: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new(); + let mut package_by_name: IndexMap<String, BasePackageHandle> = IndexMap::new(); if let PhpMixed::List(list) = locked_packages { for info in list { if let PhpMixed::Array(m) = info.as_ref() { let info_map: IndexMap<String, PhpMixed> = m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect(); let package = self.loader.load(info_map, None)?; - // TODO(phase-b): PHP shares the package between repository and map (Rc<dyn BasePackage>) - let _name = package.get_name().to_string(); + // PHP shares the package between repository and map; the handle is the shared Rc. + let _name = package.get_name(); let _ = (&mut packages, &mut package_by_name, package); todo!( "packages.add_package(package); package_by_name.insert(name, package); + AliasPackage downcast" @@ -245,10 +245,11 @@ impl Locker { .and_then(|v| v.as_string()) .unwrap_or("") .to_string(); - // TODO(phase-b): Box<dyn BasePackage> is not Clone; PHP semantics need Rc<dyn BasePackage> if let Some(base_pkg) = package_by_name.get(&alias_pkg_name) { let mut alias_pkg = CompleteAliasPackage::new( - todo!("phase-b: downcast Box<BasePackage> to CompletePackage"), + todo!( + "phase-c: narrow base_pkg handle to CompletePackageHandle" + ), m.get("alias_normalized") .and_then(|v| v.as_string()) .unwrap_or("") @@ -463,8 +464,8 @@ impl Locker { /// Locks provided data into lockfile. pub fn set_lock_data( &mut self, - packages: Vec<Box<dyn PackageInterface>>, - dev_packages: Option<Vec<Box<dyn PackageInterface>>>, + packages: Vec<PackageInterfaceHandle>, + dev_packages: Option<Vec<PackageInterfaceHandle>>, platform_reqs: IndexMap<String, String>, platform_dev_reqs: IndexMap<String, String>, aliases: Vec<IndexMap<String, PhpMixed>>, @@ -743,13 +744,11 @@ impl Locker { } /// @param PackageInterface[] $packages - fn lock_packages(&mut self, packages: &[Box<dyn PackageInterface>]) -> Result<PhpMixed> { + fn lock_packages(&mut self, packages: &[PackageInterfaceHandle]) -> Result<PhpMixed> { let mut locked: Vec<IndexMap<String, PhpMixed>> = vec![]; for package in packages { - // TODO(phase-b): `$package instanceof AliasPackage` downcast - let package_as_alias: Option<&AliasPackage> = None; - if package_as_alias.is_some() { + if package.as_alias().is_some() { continue; } @@ -767,15 +766,20 @@ impl Locker { .into()); } - let mut spec = self.dumper.dump(&**package); + let mut spec = self + .dumper + .dump(package.as_rc().borrow().as_package_interface()); spec.shift_remove("version_normalized"); // always move time to the end of the package definition let time = spec.get("time").cloned(); spec.shift_remove("time"); - let time = if package.is_dev() && package.get_installation_source() == Some("source") { + let time = if package.is_dev() + && package.get_installation_source() == Some("source".to_string()) + { // use the exact commit time of the current reference if it's a dev package - let pkg_time = self.get_package_time(&**package)?; + let pkg_time = + self.get_package_time(package.as_rc().borrow().as_package_interface())?; pkg_time.map(PhpMixed::String).or(time) } else { time @@ -976,11 +980,10 @@ impl Locker { .find_packages_with_replacers_and_providers(&link.get_target(), None); if !results.is_empty() { - // TODO(phase-b): reset_first requires Clone on dyn BasePackage; PHP returns shared reference - let provider: &Box<dyn BasePackage> = - todo!("reset_first(&results) shared ref"); + // PHP `reset($results)` returns the first shared package; clone the handle. + let provider: BasePackageHandle = results.first().unwrap().clone(); let _ = &results; - let mut description = provider.get_pretty_version().to_string(); + let mut description = provider.get_pretty_version(); if provider.get_name() != link.get_target() { 'outer: for (method, text) in [ ("getReplaces", "replaced as %s by %s"), diff --git a/crates/shirabe/src/package/mod.rs b/crates/shirabe/src/package/mod.rs index 611f36a..903056f 100644 --- a/crates/shirabe/src/package/mod.rs +++ b/crates/shirabe/src/package/mod.rs @@ -6,6 +6,7 @@ pub mod complete_alias_package; pub mod complete_package; pub mod complete_package_interface; pub mod dumper; +pub mod handle; pub mod link; pub mod loader; pub mod locker; @@ -24,6 +25,7 @@ pub use complete_alias_package::*; pub use complete_package::*; pub use complete_package_interface::*; pub use dumper::*; +pub use handle::*; pub use link::*; pub use loader::*; pub use locker::*; diff --git a/crates/shirabe/src/package/package.rs b/crates/shirabe/src/package/package.rs index 75b4e54..da8451e 100644 --- a/crates/shirabe/src/package/package.rs +++ b/crates/shirabe/src/package/package.rs @@ -570,10 +570,6 @@ impl BasePackage for Package { fn take_repository(&mut self) -> Option<Box<dyn RepositoryInterface>> { todo!() } - - fn clone_box(&self) -> Box<dyn BasePackage> { - todo!() - } } impl std::fmt::Display for Package { diff --git a/crates/shirabe/src/package/package_interface.rs b/crates/shirabe/src/package/package_interface.rs index 1c9cf2e..97aac26 100644 --- a/crates/shirabe/src/package/package_interface.rs +++ b/crates/shirabe/src/package/package_interface.rs @@ -290,14 +290,6 @@ pub trait PackageInterface: std::fmt::Display + std::fmt::Debug { /// 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::AliasPackage> { None } diff --git a/crates/shirabe/src/package/root_alias_package.rs b/crates/shirabe/src/package/root_alias_package.rs index d6dafa9..7298c1f 100644 --- a/crates/shirabe/src/package/root_alias_package.rs +++ b/crates/shirabe/src/package/root_alias_package.rs @@ -1,51 +1,77 @@ //! ref: composer/src/Composer/Package/RootAliasPackage.php -use chrono::{DateTime, Utc}; use indexmap::IndexMap; use shirabe_php_shim::PhpMixed; use crate::package::CompleteAliasPackage; +use crate::package::CompletePackageHandle; use crate::package::CompletePackageInterface; use crate::package::Link; -use crate::package::PackageInterface; -use crate::package::RootPackage; +use crate::package::RootPackageHandle; use crate::package::RootPackageInterface; -use crate::repository::RepositoryInterface; +use crate::package::handle::delegate_package_interface_to_inner; #[derive(Debug)] pub struct RootAliasPackage { inner: CompleteAliasPackage, // overrides CompleteAliasPackage::alias_of with the more specific RootPackage type - pub(crate) alias_of: RootPackage, + pub(crate) alias_of: RootPackageHandle, } impl RootAliasPackage { - pub fn new(alias_of: RootPackage, version: String, pretty_version: String) -> Self { - // TODO(phase-b): RootPackage.inner (CompletePackage) is not accessible here - let inner: CompleteAliasPackage = todo!(); + pub fn new(alias_of: RootPackageHandle, version: String, pretty_version: String) -> Self { + let inner = CompleteAliasPackage::new( + CompletePackageHandle::from(alias_of.clone()), + version, + pretty_version, + ); Self { inner, alias_of } } - pub fn get_alias_of(&self) -> &RootPackage { - &self.alias_of + pub fn get_alias_of(&self) -> RootPackageHandle { + self.alias_of.clone() + } + + pub fn set_root_package_alias(&mut self, value: bool) { + self.inner.set_root_package_alias(value); + } + + pub fn is_root_package_alias(&self) -> bool { + self.inner.is_root_package_alias() + } + + pub fn has_self_version_requires(&self) -> bool { + self.inner.has_self_version_requires() + } +} + +delegate_package_interface_to_inner!(RootAliasPackage, inner); + +impl std::fmt::Display for RootAliasPackage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self.inner, f) } } impl RootPackageInterface for RootAliasPackage { fn get_aliases(&self) -> &[IndexMap<String, String>] { - self.alias_of.get_aliases() + todo!("RootAliasPackage::get_aliases cannot return a borrow across the aliasOf handle") } fn get_minimum_stability(&self) -> &str { - self.alias_of.get_minimum_stability() + todo!( + "RootAliasPackage::get_minimum_stability cannot return &str across the aliasOf handle" + ) } fn get_stability_flags(&self) -> &IndexMap<String, i64> { - self.alias_of.get_stability_flags() + todo!( + "RootAliasPackage::get_stability_flags cannot return a borrow across the aliasOf handle" + ) } fn get_references(&self) -> &IndexMap<String, String> { - self.alias_of.get_references() + todo!("RootAliasPackage::get_references cannot return a borrow across the aliasOf handle") } fn get_prefer_stable(&self) -> bool { @@ -53,31 +79,28 @@ impl RootPackageInterface for RootAliasPackage { } fn get_config(&self) -> &IndexMap<String, PhpMixed> { - self.alias_of.get_config() + todo!("RootAliasPackage::get_config cannot return a borrow across the aliasOf handle") } fn set_requires(&mut self, requires: Vec<Link>) { - // TODO(phase-b): self.inner.requires = self.replace_self_version_dependencies(requires.clone(), Link::TYPE_REQUIRE) + // TODO(phase-c): PHP re-derives the local links via + // replaceSelfVersionDependencies before forwarding to aliasOf. self.alias_of.set_requires(requires); } fn set_dev_requires(&mut self, dev_requires: Vec<Link>) { - // TODO(phase-b): self.inner.dev_requires = self.replace_self_version_dependencies(dev_requires.clone(), Link::TYPE_DEV_REQUIRE) self.alias_of.set_dev_requires(dev_requires); } fn set_conflicts(&mut self, conflicts: Vec<Link>) { - // TODO(phase-b): self.inner.conflicts = self.replace_self_version_dependencies(conflicts.clone(), Link::TYPE_CONFLICT) self.alias_of.set_conflicts(conflicts); } fn set_provides(&mut self, provides: Vec<Link>) { - // TODO(phase-b): self.inner.provides = self.replace_self_version_dependencies(provides.clone(), Link::TYPE_PROVIDE) self.alias_of.set_provides(provides); } fn set_replaces(&mut self, replaces: Vec<Link>) { - // TODO(phase-b): self.inner.replaces = self.replace_self_version_dependencies(replaces.clone(), Link::TYPE_REPLACE) self.alias_of.set_replaces(replaces); } @@ -124,274 +147,102 @@ impl RootPackageInterface for RootAliasPackage { impl CompletePackageInterface for RootAliasPackage { fn get_scripts(&self) -> IndexMap<String, Vec<String>> { - todo!() + self.inner.get_scripts() } fn set_scripts(&mut self, scripts: IndexMap<String, Vec<String>>) { - todo!() + self.inner.set_scripts(scripts); } fn get_repositories(&self) -> Vec<IndexMap<String, PhpMixed>> { - todo!() + self.inner.get_repositories() } fn set_repositories(&mut self, repositories: Vec<IndexMap<String, PhpMixed>>) { - todo!() + self.inner.set_repositories(repositories); } fn get_license(&self) -> Vec<String> { - todo!() + self.inner.get_license() } fn set_license(&mut self, license: Vec<String>) { - todo!() + self.inner.set_license(license); } fn get_keywords(&self) -> Vec<String> { - todo!() + self.inner.get_keywords() } fn set_keywords(&mut self, keywords: Vec<String>) { - todo!() + self.inner.set_keywords(keywords); } fn get_description(&self) -> Option<&str> { - todo!() + self.inner.get_description() } fn set_description(&mut self, description: String) { - todo!() + self.inner.set_description(description); } fn get_homepage(&self) -> Option<&str> { - todo!() + self.inner.get_homepage() } fn set_homepage(&mut self, homepage: String) { - todo!() + self.inner.set_homepage(homepage); } fn get_authors(&self) -> Vec<IndexMap<String, String>> { - todo!() + self.inner.get_authors() } fn set_authors(&mut self, authors: Vec<IndexMap<String, String>>) { - todo!() + self.inner.set_authors(authors); } fn get_support(&self) -> IndexMap<String, String> { - todo!() + self.inner.get_support() } fn set_support(&mut self, support: IndexMap<String, String>) { - todo!() + self.inner.set_support(support); } fn get_funding(&self) -> Vec<IndexMap<String, PhpMixed>> { - todo!() + self.inner.get_funding() } fn set_funding(&mut self, funding: Vec<IndexMap<String, PhpMixed>>) { - todo!() + self.inner.set_funding(funding); } fn is_abandoned(&self) -> bool { - todo!() + self.inner.is_abandoned() } fn get_replacement_package(&self) -> Option<&str> { - todo!() + self.inner.get_replacement_package() } fn set_abandoned(&mut self, abandoned: PhpMixed) { - todo!() + self.inner.set_abandoned(abandoned); } fn get_archive_name(&self) -> Option<&str> { - todo!() + self.inner.get_archive_name() } fn set_archive_name(&mut self, name: String) { - todo!() + self.inner.set_archive_name(name); } fn get_archive_excludes(&self) -> Vec<String> { - todo!() + self.inner.get_archive_excludes() } fn set_archive_excludes(&mut self, excludes: Vec<String>) { - todo!() - } -} - -impl std::fmt::Display for RootAliasPackage { - fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - todo!() - } -} - -impl PackageInterface for RootAliasPackage { - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn get_name(&self) -> &str { - todo!() - } - fn get_pretty_name(&self) -> &str { - todo!() - } - fn get_names(&self, _provides: bool) -> Vec<String> { - todo!() - } - fn set_id(&mut self, _id: i64) { - todo!() - } - fn get_id(&self) -> i64 { - todo!() - } - fn is_dev(&self) -> bool { - todo!() - } - fn get_type(&self) -> &str { - todo!() - } - fn get_target_dir(&self) -> Option<&str> { - todo!() - } - fn get_extra(&self) -> IndexMap<String, PhpMixed> { - todo!() - } - fn set_installation_source(&mut self, _type: Option<String>) { - todo!() - } - fn get_installation_source(&self) -> Option<&str> { - todo!() - } - fn get_source_type(&self) -> Option<&str> { - todo!() - } - fn get_source_url(&self) -> Option<&str> { - todo!() - } - fn get_source_urls(&self) -> Vec<String> { - todo!() - } - fn get_source_reference(&self) -> Option<&str> { - todo!() - } - fn get_source_mirrors(&self) -> Option<Vec<IndexMap<String, PhpMixed>>> { - todo!() - } - fn set_source_mirrors(&mut self, _mirrors: Option<Vec<IndexMap<String, PhpMixed>>>) { - todo!() - } - fn get_dist_type(&self) -> Option<&str> { - todo!() - } - fn get_dist_url(&self) -> Option<&str> { - todo!() - } - fn get_dist_urls(&self) -> Vec<String> { - todo!() - } - fn get_dist_reference(&self) -> Option<&str> { - todo!() - } - fn get_dist_sha1_checksum(&self) -> Option<&str> { - todo!() - } - fn get_dist_mirrors(&self) -> Option<Vec<IndexMap<String, PhpMixed>>> { - todo!() - } - fn set_dist_mirrors(&mut self, _mirrors: Option<Vec<IndexMap<String, PhpMixed>>>) { - todo!() - } - fn get_version(&self) -> &str { - todo!() - } - fn get_pretty_version(&self) -> &str { - todo!() - } - fn get_full_pretty_version(&self, _truncate: bool, _display_mode: i64) -> String { - todo!() - } - fn get_release_date(&self) -> Option<DateTime<Utc>> { - todo!() - } - fn get_stability(&self) -> &str { - todo!() - } - fn get_requires(&self) -> IndexMap<String, Link> { - todo!() - } - fn get_conflicts(&self) -> IndexMap<String, Link> { - todo!() - } - fn get_provides(&self) -> IndexMap<String, Link> { - todo!() - } - fn get_replaces(&self) -> IndexMap<String, Link> { - todo!() - } - fn get_dev_requires(&self) -> IndexMap<String, Link> { - todo!() - } - fn get_suggests(&self) -> IndexMap<String, String> { - todo!() - } - fn get_autoload(&self) -> IndexMap<String, PhpMixed> { - todo!() - } - fn get_dev_autoload(&self) -> IndexMap<String, PhpMixed> { - todo!() - } - fn get_include_paths(&self) -> Vec<String> { - todo!() - } - fn get_php_ext(&self) -> Option<IndexMap<String, PhpMixed>> { - todo!() - } - fn set_repository(&mut self, _repository: Box<dyn RepositoryInterface>) -> anyhow::Result<()> { - todo!() - } - fn get_repository(&self) -> Option<&dyn RepositoryInterface> { - todo!() - } - fn get_binaries(&self) -> Vec<String> { - todo!() - } - fn get_unique_name(&self) -> String { - todo!() - } - fn get_notification_url(&self) -> Option<&str> { - todo!() - } - fn get_pretty_string(&self) -> String { - todo!() - } - fn is_default_branch(&self) -> bool { - todo!() - } - fn get_transport_options(&self) -> IndexMap<String, PhpMixed> { - todo!() - } - fn set_transport_options(&mut self, _options: IndexMap<String, PhpMixed>) { - todo!() - } - fn set_source_reference(&mut self, _reference: Option<String>) { - todo!() - } - fn set_dist_url(&mut self, _url: Option<String>) { - todo!() - } - fn set_dist_type(&mut self, _type: Option<String>) { - todo!() - } - fn set_dist_reference(&mut self, _reference: Option<String>) { - todo!() - } - fn set_source_dist_references(&mut self, _reference: &str) { - todo!() + self.inner.set_archive_excludes(excludes); } } diff --git a/crates/shirabe/src/package/root_package.rs b/crates/shirabe/src/package/root_package.rs index 7e79530..b0fdf26 100644 --- a/crates/shirabe/src/package/root_package.rs +++ b/crates/shirabe/src/package/root_package.rs @@ -26,9 +26,7 @@ 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); + let inner = CompletePackage::new(name, version, pretty_version); Self { inner, minimum_stability: "stable".to_string(), diff --git a/crates/shirabe/src/package/root_package_interface.rs b/crates/shirabe/src/package/root_package_interface.rs index 2370e25..5f47232 100644 --- a/crates/shirabe/src/package/root_package_interface.rs +++ b/crates/shirabe/src/package/root_package_interface.rs @@ -51,14 +51,6 @@ pub trait RootPackageInterface: CompletePackageInterface { 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/version_selector.rs b/crates/shirabe/src/package/version/version_selector.rs index 3bd0191..28b9277 100644 --- a/crates/shirabe/src/package/version/version_selector.rs +++ b/crates/shirabe/src/package/version/version_selector.rs @@ -16,9 +16,8 @@ use crate::filter::platform_requirement_filter::IgnoreListPlatformRequirementFil use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory; use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface; use crate::io::IOInterface; -use crate::package::AliasPackage; use crate::package::PackageInterface; -use crate::package::base_package::{self, BasePackage}; +use crate::package::base_package; use crate::package::dumper::ArrayDumper; use crate::package::loader::ArrayLoader; use crate::package::version::VersionParser; @@ -69,7 +68,7 @@ impl VersionSelector { repo_set_flags: i64, io: Option<&dyn IOInterface>, show_warnings: shirabe_php_shim::PhpMixed, - ) -> anyhow::Result<Option<Box<dyn PackageInterface>>> { + ) -> anyhow::Result<Option<crate::package::PackageInterfaceHandle>> { if !base_package::STABILITIES.contains_key(preferred_stability) { return Err(shirabe_php_shim::UnexpectedValueException { message: format!( @@ -99,8 +98,14 @@ impl VersionSelector { let min_priority = *base_package::STABILITIES.get(preferred_stability).unwrap(); candidates.sort_by(|a, b| { - let a_priority = a.get_stability_priority(); - let b_priority = b.get_stability_priority(); + // BasePackage::get_stability_priority() is not forwarded by the handle; compute it + // directly from the stability name. + let a_priority = *base_package::STABILITIES + .get(a.get_stability().as_str()) + .unwrap(); + let b_priority = *base_package::STABILITIES + .get(b.get_stability().as_str()) + .unwrap(); if min_priority < a_priority && b_priority < a_priority { return std::cmp::Ordering::Greater; @@ -112,9 +117,9 @@ impl VersionSelector { return std::cmp::Ordering::Less; } - if version_compare(b.get_version(), a.get_version(), ">") { + if version_compare(&b.get_version(), &a.get_version(), ">") { std::cmp::Ordering::Greater - } else if version_compare(b.get_version(), a.get_version(), "<") { + } else if version_compare(&b.get_version(), &a.get_version(), "<") { std::cmp::Ordering::Less } else { std::cmp::Ordering::Equal @@ -127,11 +132,11 @@ impl VersionSelector { .downcast_ref::<IgnoreAllPlatformRequirementFilter>() .is_some(); - let package: Option<Box<dyn PackageInterface>>; + let package: Option<crate::package::PackageInterfaceHandle>; if !self.platform_constraints.is_empty() && !is_ignore_all { let mut already_warned_names: IndexMap<String, bool> = IndexMap::new(); let mut already_seen_names: IndexMap<String, bool> = IndexMap::new(); - let mut found_package: Option<Box<dyn PackageInterface>> = None; + let mut found_package: Option<crate::package::PackageInterfaceHandle> = None; 'pkgs: for pkg in candidates.iter() { let reqs = pkg.get_requires(); @@ -171,7 +176,7 @@ impl VersionSelector { reason = "is missing from your platform"; } - let is_latest_version = !already_seen_names.contains_key(pkg.get_name()); + let is_latest_version = !already_seen_names.contains_key(&pkg.get_name()); already_seen_names.insert(pkg.get_name().to_string(), true); if let Some(io) = io { let should_warn = match &show_warnings { @@ -215,13 +220,13 @@ impl VersionSelector { continue; } - found_package = Some(pkg.clone_box()); + found_package = Some(pkg.clone().into()); break; } package = found_package; } else { package = if !candidates.is_empty() { - Some(candidates.remove(0).clone_package_box()) + Some(candidates.remove(0).into()) } else { None }; @@ -232,10 +237,9 @@ impl VersionSelector { Some(p) => p, }; - let package = if let Some(alias) = package.as_ref().as_any().downcast_ref::<AliasPackage>() - { + let package = if let Some(alias) = package.as_alias() { if alias.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS { - alias.get_alias_of().clone_package_box() + alias.get_alias_of().into() } else { package } |
