From 4df5f8491320e5795718cf0222e80fa27e57c8ad Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 11 May 2026 02:05:34 +0900 Subject: refactor(package): rename traits and switch dep maps to IndexMap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename Package/CompletePackage to PackageInterface/CompletePackageInterface to mirror Composer's interface names, and split each into its own module under crates/mozart-core/src/package/. Switch dependency-link and metadata maps from BTreeMap to indexmap::IndexMap so serialized JSON preserves the original key ordering rather than sorting alphabetically — matching PHP associative-array semantics. The --sort-packages behaviour in `require` is preserved via sort_unstable_keys. --- crates/mozart-core/src/package.rs | 252 ++++++++++++++++---------------------- 1 file changed, 103 insertions(+), 149 deletions(-) (limited to 'crates/mozart-core/src/package.rs') diff --git a/crates/mozart-core/src/package.rs b/crates/mozart-core/src/package.rs index c6a3ae9..de88ffc 100644 --- a/crates/mozart-core/src/package.rs +++ b/crates/mozart-core/src/package.rs @@ -1,14 +1,18 @@ use serde::de::{Deserializer, MapAccess, SeqAccess, Visitor}; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; use std::fmt; use std::fs; use std::path::Path; pub mod archiver; +mod complete_package_interface; pub mod dumper; +mod package_interface; pub mod version; +pub use complete_package_interface::*; +pub use package_interface::*; + /// Package stability level. /// Higher value = less stable. /// Corresponds to `Composer\Package\BasePackage::STABILITY_*`. @@ -62,8 +66,8 @@ pub struct Author { /// Autoload rule sets (PSR-4, PSR-0, classmap, files). #[derive(Debug, Clone, Default)] pub struct AutoloadRules { - pub psr4: BTreeMap>, - pub psr0: BTreeMap>, + pub psr4: indexmap::IndexMap>, + pub psr0: indexmap::IndexMap>, pub classmap: Vec, pub files: Vec, } @@ -125,19 +129,19 @@ pub struct PackageData { pub dist_sha1_checksum: Option, pub release_date: Option, - pub extra: BTreeMap, + pub extra: indexmap::IndexMap, pub binaries: Vec, pub dev: bool, pub stability: Stability, pub notification_url: Option, // dependency links - pub requires: BTreeMap, - pub conflicts: BTreeMap, - pub provides: BTreeMap, - pub replaces: BTreeMap, - pub dev_requires: BTreeMap, - pub suggests: BTreeMap, + pub requires: indexmap::IndexMap, + pub conflicts: indexmap::IndexMap, + pub provides: indexmap::IndexMap, + pub replaces: indexmap::IndexMap, + pub dev_requires: indexmap::IndexMap, + pub suggests: indexmap::IndexMap, // autoload pub autoload: AutoloadRules, @@ -157,7 +161,7 @@ pub struct CompletePackageData { pub license: Vec, pub keywords: Vec, pub authors: Vec, - pub scripts: BTreeMap>, + pub scripts: indexmap::IndexMap>, pub support: Support, pub funding: Vec, pub repositories: Vec, @@ -175,105 +179,55 @@ pub struct RootPackageData { pub minimum_stability: Stability, pub prefer_stable: bool, - pub stability_flags: BTreeMap, - pub config: BTreeMap, - pub references: BTreeMap, + pub stability_flags: indexmap::IndexMap, + pub config: indexmap::IndexMap, + pub references: indexmap::IndexMap, pub aliases: Vec, } -/// Accessor for `PackageData` fields. -/// Corresponds to `Composer\Package\PackageInterface`. -pub trait Package { - fn name(&self) -> &str; - fn pretty_name(&self) -> &str; - fn version(&self) -> &str; - fn pretty_version(&self) -> &str; - fn package_type(&self) -> &str; - fn target_dir(&self) -> Option<&str>; - fn source_type(&self) -> Option<&str>; - fn source_url(&self) -> Option<&str>; - fn source_reference(&self) -> Option<&str>; - fn dist_type(&self) -> Option<&str>; - fn dist_url(&self) -> Option<&str>; - fn dist_reference(&self) -> Option<&str>; - fn dist_sha1_checksum(&self) -> Option<&str>; - fn release_date(&self) -> Option<&str>; - fn extra(&self) -> &BTreeMap; - fn binaries(&self) -> &[String]; - fn is_dev(&self) -> bool; - fn stability(&self) -> Stability; - fn notification_url(&self) -> Option<&str>; - fn requires(&self) -> &BTreeMap; - fn conflicts(&self) -> &BTreeMap; - fn provides(&self) -> &BTreeMap; - fn replaces(&self) -> &BTreeMap; - fn dev_requires(&self) -> &BTreeMap; - fn suggests(&self) -> &BTreeMap; - fn autoload(&self) -> &AutoloadRules; - fn dev_autoload(&self) -> &AutoloadRules; - fn is_default_branch(&self) -> bool; -} - -/// Accessor for `CompletePackageData` fields. -/// Corresponds to `Composer\Package\CompletePackageInterface`. -pub trait CompletePackage: Package { - fn description(&self) -> Option<&str>; - fn homepage(&self) -> Option<&str>; - fn license(&self) -> &[String]; - fn keywords(&self) -> &[String]; - fn authors(&self) -> &[Author]; - fn scripts(&self) -> &BTreeMap>; - fn support(&self) -> &Support; - fn funding(&self) -> &[Funding]; - fn repositories(&self) -> &[serde_json::Value]; - fn abandoned(&self) -> Option<&str>; - fn archive_name(&self) -> Option<&str>; - fn archive_excludes(&self) -> &[String]; -} - /// Accessor for `RootPackageData` fields. /// Corresponds to `Composer\Package\RootPackageInterface`. -pub trait RootPackage: CompletePackage { +pub trait RootPackage: CompletePackageInterface { fn minimum_stability(&self) -> Stability; fn prefer_stable(&self) -> bool; - fn stability_flags(&self) -> &BTreeMap; - fn config(&self) -> &BTreeMap; - fn references(&self) -> &BTreeMap; + fn stability_flags(&self) -> &indexmap::IndexMap; + fn config(&self) -> &indexmap::IndexMap; + fn references(&self) -> &indexmap::IndexMap; fn aliases(&self) -> &[VersionAlias]; } /// Implements `Package` trait by delegating to an inner `PackageData` field. macro_rules! delegate_package { ($type:ty => $($path:ident).+) => { - impl Package for $type { - fn name(&self) -> &str { &self.$($path).+.name } - fn pretty_name(&self) -> &str { &self.$($path).+.pretty_name } - fn version(&self) -> &str { &self.$($path).+.version } - fn pretty_version(&self) -> &str { &self.$($path).+.pretty_version } - fn package_type(&self) -> &str { &self.$($path).+.package_type } - fn target_dir(&self) -> Option<&str> { self.$($path).+.target_dir.as_deref() } - fn source_type(&self) -> Option<&str> { self.$($path).+.source_type.as_deref() } - fn source_url(&self) -> Option<&str> { self.$($path).+.source_url.as_deref() } - fn source_reference(&self) -> Option<&str> { self.$($path).+.source_reference.as_deref() } - fn dist_type(&self) -> Option<&str> { self.$($path).+.dist_type.as_deref() } - fn dist_url(&self) -> Option<&str> { self.$($path).+.dist_url.as_deref() } - fn dist_reference(&self) -> Option<&str> { self.$($path).+.dist_reference.as_deref() } - fn dist_sha1_checksum(&self) -> Option<&str> { self.$($path).+.dist_sha1_checksum.as_deref() } - fn release_date(&self) -> Option<&str> { self.$($path).+.release_date.as_deref() } - fn extra(&self) -> &BTreeMap { &self.$($path).+.extra } - fn binaries(&self) -> &[String] { &self.$($path).+.binaries } - fn is_dev(&self) -> bool { self.$($path).+.dev } - fn stability(&self) -> Stability { self.$($path).+.stability } - fn notification_url(&self) -> Option<&str> { self.$($path).+.notification_url.as_deref() } - fn requires(&self) -> &BTreeMap { &self.$($path).+.requires } - fn conflicts(&self) -> &BTreeMap { &self.$($path).+.conflicts } - fn provides(&self) -> &BTreeMap { &self.$($path).+.provides } - fn replaces(&self) -> &BTreeMap { &self.$($path).+.replaces } - fn dev_requires(&self) -> &BTreeMap { &self.$($path).+.dev_requires } - fn suggests(&self) -> &BTreeMap { &self.$($path).+.suggests } - fn autoload(&self) -> &AutoloadRules { &self.$($path).+.autoload } - fn dev_autoload(&self) -> &AutoloadRules { &self.$($path).+.dev_autoload } - fn is_default_branch(&self) -> bool { self.$($path).+.is_default_branch } + impl PackageInterface for $type { + fn name(&self) -> &str { &self.$($path).+.name } + fn pretty_name(&self) -> &str { &self.$($path).+.pretty_name } + fn version(&self) -> &str { &self.$($path).+.version } + fn pretty_version(&self) -> &str { &self.$($path).+.pretty_version } + fn package_type(&self) -> &str { &self.$($path).+.package_type } + fn target_dir(&self) -> Option<&str> { self.$($path).+.target_dir.as_deref() } + fn source_type(&self) -> Option<&str> { self.$($path).+.source_type.as_deref() } + fn source_url(&self) -> Option<&str> { self.$($path).+.source_url.as_deref() } + fn source_reference(&self) -> Option<&str> { self.$($path).+.source_reference.as_deref() } + fn dist_type(&self) -> Option<&str> { self.$($path).+.dist_type.as_deref() } + fn dist_url(&self) -> Option<&str> { self.$($path).+.dist_url.as_deref() } + fn dist_reference(&self) -> Option<&str> { self.$($path).+.dist_reference.as_deref() } + fn dist_sha1_checksum(&self) -> Option<&str> { self.$($path).+.dist_sha1_checksum.as_deref() } + fn release_date(&self) -> Option<&str> { self.$($path).+.release_date.as_deref() } + fn extra(&self) -> &indexmap::IndexMap { &self.$($path).+.extra } + fn binaries(&self) -> &[String] { &self.$($path).+.binaries } + fn is_dev(&self) -> bool { self.$($path).+.dev } + fn stability(&self) -> Stability { self.$($path).+.stability } + fn notification_url(&self) -> Option<&str> { self.$($path).+.notification_url.as_deref() } + fn requires(&self) -> &indexmap::IndexMap { &self.$($path).+.requires } + fn conflicts(&self) -> &indexmap::IndexMap { &self.$($path).+.conflicts } + fn provides(&self) -> &indexmap::IndexMap { &self.$($path).+.provides } + fn replaces(&self) -> &indexmap::IndexMap { &self.$($path).+.replaces } + fn dev_requires(&self) -> &indexmap::IndexMap { &self.$($path).+.dev_requires } + fn suggests(&self) -> &indexmap::IndexMap { &self.$($path).+.suggests } + fn autoload(&self) -> &AutoloadRules { &self.$($path).+.autoload } + fn dev_autoload(&self) -> &AutoloadRules { &self.$($path).+.dev_autoload } + fn is_default_branch(&self) -> bool { self.$($path).+.is_default_branch } } }; } @@ -281,24 +235,24 @@ macro_rules! delegate_package { /// Implements `CompletePackage` trait by delegating to an inner `CompletePackageData` field. macro_rules! delegate_complete_package { ($type:ty => $($path:ident).+) => { - impl CompletePackage for $type { - fn description(&self) -> Option<&str> { self.$($path).+.description.as_deref() } - fn homepage(&self) -> Option<&str> { self.$($path).+.homepage.as_deref() } - fn license(&self) -> &[String] { &self.$($path).+.license } - fn keywords(&self) -> &[String] { &self.$($path).+.keywords } - fn authors(&self) -> &[Author] { &self.$($path).+.authors } - fn scripts(&self) -> &BTreeMap> { &self.$($path).+.scripts } - fn support(&self) -> &Support { &self.$($path).+.support } - fn funding(&self) -> &[Funding] { &self.$($path).+.funding } - fn repositories(&self) -> &[serde_json::Value] { &self.$($path).+.repositories } - fn abandoned(&self) -> Option<&str> { self.$($path).+.abandoned.as_deref() } - fn archive_name(&self) -> Option<&str> { self.$($path).+.archive_name.as_deref() } - fn archive_excludes(&self) -> &[String] { &self.$($path).+.archive_excludes } + impl CompletePackageInterface for $type { + fn description(&self) -> Option<&str> { self.$($path).+.description.as_deref() } + fn homepage(&self) -> Option<&str> { self.$($path).+.homepage.as_deref() } + fn license(&self) -> &[String] { &self.$($path).+.license } + fn keywords(&self) -> &[String] { &self.$($path).+.keywords } + fn authors(&self) -> &[Author] { &self.$($path).+.authors } + fn scripts(&self) -> &indexmap::IndexMap> { &self.$($path).+.scripts } + fn support(&self) -> &Support { &self.$($path).+.support } + fn funding(&self) -> &[Funding] { &self.$($path).+.funding } + fn repositories(&self) -> &[serde_json::Value] { &self.$($path).+.repositories } + fn abandoned(&self) -> Option<&str> { self.$($path).+.abandoned.as_deref() } + fn archive_name(&self) -> Option<&str> { self.$($path).+.archive_name.as_deref() } + fn archive_excludes(&self) -> &[String] { &self.$($path).+.archive_excludes } } }; } -impl Package for PackageData { +impl PackageInterface for PackageData { fn name(&self) -> &str { &self.name } @@ -341,7 +295,7 @@ impl Package for PackageData { fn release_date(&self) -> Option<&str> { self.release_date.as_deref() } - fn extra(&self) -> &BTreeMap { + fn extra(&self) -> &indexmap::IndexMap { &self.extra } fn binaries(&self) -> &[String] { @@ -356,22 +310,22 @@ impl Package for PackageData { fn notification_url(&self) -> Option<&str> { self.notification_url.as_deref() } - fn requires(&self) -> &BTreeMap { + fn requires(&self) -> &indexmap::IndexMap { &self.requires } - fn conflicts(&self) -> &BTreeMap { + fn conflicts(&self) -> &indexmap::IndexMap { &self.conflicts } - fn provides(&self) -> &BTreeMap { + fn provides(&self) -> &indexmap::IndexMap { &self.provides } - fn replaces(&self) -> &BTreeMap { + fn replaces(&self) -> &indexmap::IndexMap { &self.replaces } - fn dev_requires(&self) -> &BTreeMap { + fn dev_requires(&self) -> &indexmap::IndexMap { &self.dev_requires } - fn suggests(&self) -> &BTreeMap { + fn suggests(&self) -> &indexmap::IndexMap { &self.suggests } fn autoload(&self) -> &AutoloadRules { @@ -385,7 +339,7 @@ impl Package for PackageData { } } -impl CompletePackage for CompletePackageData { +impl CompletePackageInterface for CompletePackageData { fn description(&self) -> Option<&str> { self.description.as_deref() } @@ -401,7 +355,7 @@ impl CompletePackage for CompletePackageData { fn authors(&self) -> &[Author] { &self.authors } - fn scripts(&self) -> &BTreeMap> { + fn scripts(&self) -> &indexmap::IndexMap> { &self.scripts } fn support(&self) -> &Support { @@ -431,13 +385,13 @@ impl RootPackage for RootPackageData { fn prefer_stable(&self) -> bool { self.prefer_stable } - fn stability_flags(&self) -> &BTreeMap { + fn stability_flags(&self) -> &indexmap::IndexMap { &self.stability_flags } - fn config(&self) -> &BTreeMap { + fn config(&self) -> &indexmap::IndexMap { &self.config } - fn references(&self) -> &BTreeMap { + fn references(&self) -> &indexmap::IndexMap { &self.references } fn aliases(&self) -> &[VersionAlias] { @@ -489,23 +443,23 @@ pub struct RawPackageData { pub minimum_stability: Option, #[serde(default)] - pub require: BTreeMap, + pub require: indexmap::IndexMap, #[serde( rename = "require-dev", default, - skip_serializing_if = "BTreeMap::is_empty" + skip_serializing_if = "indexmap::IndexMap::is_empty" )] - pub require_dev: BTreeMap, + pub require_dev: indexmap::IndexMap, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub conflict: BTreeMap, + #[serde(default, skip_serializing_if = "indexmap::IndexMap::is_empty")] + pub conflict: indexmap::IndexMap, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub provide: BTreeMap, + #[serde(default, skip_serializing_if = "indexmap::IndexMap::is_empty")] + pub provide: indexmap::IndexMap, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub replace: BTreeMap, + #[serde(default, skip_serializing_if = "indexmap::IndexMap::is_empty")] + pub replace: indexmap::IndexMap, /// `repositories` accepts either a JSON array or a JSON object keyed by /// repository name. Composer iterates `foreach ($repoConfigs as ...)` in @@ -525,7 +479,7 @@ pub struct RawPackageData { pub bin: Vec, #[serde(flatten)] - pub extra_fields: BTreeMap, + pub extra_fields: indexmap::IndexMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -539,7 +493,7 @@ pub struct RawAuthor { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RawAutoload { #[serde(rename = "psr-4")] - pub psr4: BTreeMap, + pub psr4: indexmap::IndexMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -653,15 +607,15 @@ impl RawPackageData { license: None, authors: Vec::new(), minimum_stability: None, - require: BTreeMap::new(), - require_dev: BTreeMap::new(), - conflict: BTreeMap::new(), - provide: BTreeMap::new(), - replace: BTreeMap::new(), + require: indexmap::IndexMap::new(), + require_dev: indexmap::IndexMap::new(), + conflict: indexmap::IndexMap::new(), + provide: indexmap::IndexMap::new(), + replace: indexmap::IndexMap::new(), repositories: Vec::new(), autoload: None, bin: Vec::new(), - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } @@ -696,9 +650,9 @@ impl RootPackageData { pub fn from_raw(raw: RawPackageData) -> Self { fn make_links( source: &str, - deps: BTreeMap, + deps: indexmap::IndexMap, description: &str, - ) -> BTreeMap { + ) -> indexmap::IndexMap { deps.into_iter() .map(|(target, constraint)| { let normalized = target.to_lowercase(); @@ -745,7 +699,7 @@ impl RootPackageData { }) .unwrap_or_default(); - let extra: BTreeMap = raw + let extra: indexmap::IndexMap = raw .extra_fields .get("extra") .and_then(|v| v.as_object()) @@ -783,7 +737,7 @@ impl RootPackageData { }) .unwrap_or_default(); - let scripts: BTreeMap> = raw + let scripts: indexmap::IndexMap> = raw .extra_fields .get("scripts") .and_then(|v| v.as_object()) @@ -841,7 +795,7 @@ impl RootPackageData { }) .unwrap_or_default(); - let config: BTreeMap = raw + let config: indexmap::IndexMap = raw .extra_fields .get("config") .and_then(|v| v.as_object()) @@ -882,7 +836,7 @@ impl RootPackageData { provides, replaces, dev_requires, - suggests: BTreeMap::new(), + suggests: indexmap::IndexMap::new(), autoload, dev_autoload: AutoloadRules::default(), is_default_branch: false, @@ -908,9 +862,9 @@ impl RootPackageData { complete: complete_data, minimum_stability, prefer_stable, - stability_flags: BTreeMap::new(), + stability_flags: indexmap::IndexMap::new(), config, - references: BTreeMap::new(), + references: indexmap::IndexMap::new(), aliases: Vec::new(), } } @@ -976,7 +930,7 @@ mod tests { security_advisories: None, }]; - let mut psr4 = BTreeMap::new(); + let mut psr4 = indexmap::IndexMap::new(); psr4.insert("Acme\\Full\\".to_string(), "src/".to_string()); raw.autoload = Some(RawAutoload { psr4 }); -- cgit v1.3.1