diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-11 02:05:34 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-11 02:05:34 +0900 |
| commit | 4df5f8491320e5795718cf0222e80fa27e57c8ad (patch) | |
| tree | 707e19f34dbdef18490ec3245d34389e3d189a57 /crates | |
| parent | 8871b923fa3df1935c263db155cb8bc3d59705cd (diff) | |
| download | php-mozart-4df5f8491320e5795718cf0222e80fa27e57c8ad.tar.gz php-mozart-4df5f8491320e5795718cf0222e80fa27e57c8ad.tar.zst php-mozart-4df5f8491320e5795718cf0222e80fa27e57c8ad.zip | |
refactor(package): rename traits and switch dep maps to IndexMap
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.
Diffstat (limited to 'crates')
29 files changed, 529 insertions, 553 deletions
diff --git a/crates/mozart-class-map-generator/Cargo.toml b/crates/mozart-class-map-generator/Cargo.toml index 465b26c..e1bd1a3 100644 --- a/crates/mozart-class-map-generator/Cargo.toml +++ b/crates/mozart-class-map-generator/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true [dependencies] anyhow.workspace = true +indexmap.workspace = true regex.workspace = true [dev-dependencies] diff --git a/crates/mozart-class-map-generator/src/classmap.rs b/crates/mozart-class-map-generator/src/classmap.rs index e1631f4..17ca04a 100644 --- a/crates/mozart-class-map-generator/src/classmap.rs +++ b/crates/mozart-class-map-generator/src/classmap.rs @@ -133,18 +133,18 @@ pub fn path_to_static_expr(file: &Path, vendor_dir: &Path, project_dir: &Path) - /// /// Returns `(dynamic_classmap, static_classmap, psr_violations)`. pub fn scan_psr_for_classmap( - psr4: &BTreeMap<String, Vec<String>>, - psr0: &BTreeMap<String, Vec<String>>, + psr4: &indexmap::IndexMap<String, Vec<String>>, + psr0: &indexmap::IndexMap<String, Vec<String>>, vendor_dir: &Path, project_dir: &Path, excluded: &[String], ) -> ( - BTreeMap<String, String>, - BTreeMap<String, String>, + indexmap::IndexMap<String, String>, + indexmap::IndexMap<String, String>, Vec<String>, ) { - let mut dyn_map: BTreeMap<String, String> = BTreeMap::new(); - let mut static_map: BTreeMap<String, String> = BTreeMap::new(); + let mut dyn_map = indexmap::IndexMap::new(); + let mut static_map = indexmap::IndexMap::new(); let mut violations: Vec<String> = Vec::new(); // Helper: resolve a PHP path expression to an absolute path. diff --git a/crates/mozart-core/src/autoload.rs b/crates/mozart-core/src/autoload.rs index 7e8ead6..08a2859 100644 --- a/crates/mozart-core/src/autoload.rs +++ b/crates/mozart-core/src/autoload.rs @@ -8,7 +8,6 @@ use crate::repository::installed::InstalledPackages; use crate::repository::lockfile::LockedPackage; use indexmap::IndexSet; use mozart_class_map_generator::{scan_classmap_dirs, scan_psr_for_classmap}; -use std::collections::BTreeMap; use std::path::{Path, PathBuf}; // Embed Composer PHP files from the submodule at compile time. @@ -70,15 +69,15 @@ pub struct AutoloadConfig { pub struct AutoloadData { /// PSR-4: namespace prefix -> list of directory path expressions. /// Each path is a PHP expression string like `$vendorDir . '/psr/log/src'`. - pub psr4: BTreeMap<String, Vec<String>>, + pub psr4: indexmap::IndexMap<String, Vec<String>>, /// PSR-0: namespace prefix -> list of directory path expressions. /// (Empty in Phase 2.2, populated in 5.6.) - pub psr0: BTreeMap<String, Vec<String>>, + pub psr0: indexmap::IndexMap<String, Vec<String>>, /// Classmap entries: class name -> file path expression. /// (Empty in Phase 2.2, populated in 5.6.) - pub classmap: BTreeMap<String, String>, + pub classmap: indexmap::IndexMap<String, String>, /// Files to include on every request: file_identifier -> path expression. - pub files: BTreeMap<String, String>, + pub files: indexmap::IndexMap<String, String>, } /// Mirror of `Composer\ClassMapGenerator\ClassMap` — the return value @@ -92,9 +91,9 @@ pub struct AutoloadData { /// summary — once `generate` is refactored to expose the full classmap /// these fields will hold the real entries. pub struct ClassMap { - map: BTreeMap<String, String>, + map: indexmap::IndexMap<String, String>, psr_violations: Vec<String>, - ambiguous_classes: BTreeMap<String, Vec<String>>, + ambiguous_classes: indexmap::IndexMap<String, Vec<String>>, } impl ClassMap { @@ -126,7 +125,7 @@ impl ClassMap { } /// Read access to the underlying map (`getMap()` upstream). - pub fn map(&self) -> &BTreeMap<String, String> { + pub fn map(&self) -> &indexmap::IndexMap<String, String> { &self.map } @@ -136,7 +135,7 @@ impl ClassMap { } /// Read access to the ambiguous-class records. - pub fn ambiguous_classes(&self) -> &BTreeMap<String, Vec<String>> { + pub fn ambiguous_classes(&self) -> &indexmap::IndexMap<String, Vec<String>> { &self.ambiguous_classes } } @@ -282,9 +281,9 @@ impl AutoloadGeneratorExt for AutoloadGenerator { // for now and surface the limitation here rather than // silently writing files. return Ok(ClassMap { - map: BTreeMap::new(), + map: indexmap::IndexMap::new(), psr_violations: Vec::new(), - ambiguous_classes: BTreeMap::new(), + ambiguous_classes: indexmap::IndexMap::new(), }); } @@ -297,7 +296,7 @@ impl AutoloadGeneratorExt for AutoloadGenerator { // command code that only branches on `count()` / `has_*()` works // today; refactoring `generate` to surface the full map is // tracked as follow-up work. - let mut map = BTreeMap::new(); + let mut map = indexmap::IndexMap::new(); for i in 0..result.class_count { map.insert(format!("__mozart_placeholder_{i}"), String::new()); } @@ -308,7 +307,7 @@ impl AutoloadGeneratorExt for AutoloadGenerator { } else { Vec::new() }; - let mut ambiguous_classes = BTreeMap::new(); + let mut ambiguous_classes = indexmap::IndexMap::new(); if result.has_ambiguous_classes { ambiguous_classes.insert("__mozart_placeholder".to_string(), Vec::new()); } @@ -526,16 +525,16 @@ fn collect_autoloads( dev_mode: bool, ) -> (AutoloadData, AutoloadData) { let mut data = AutoloadData { - psr4: BTreeMap::new(), - psr0: BTreeMap::new(), - classmap: BTreeMap::new(), - files: BTreeMap::new(), + psr4: indexmap::IndexMap::new(), + psr0: indexmap::IndexMap::new(), + classmap: indexmap::IndexMap::new(), + files: indexmap::IndexMap::new(), }; let mut static_data = AutoloadData { - psr4: BTreeMap::new(), - psr0: BTreeMap::new(), - classmap: BTreeMap::new(), - files: BTreeMap::new(), + psr4: indexmap::IndexMap::new(), + psr0: indexmap::IndexMap::new(), + classmap: indexmap::IndexMap::new(), + files: indexmap::IndexMap::new(), }; // Process each installed package @@ -703,7 +702,7 @@ fn generate_autoload_static(static_data: &AutoloadData, suffix: &str) -> String // $prefixLengthsPsr4 — group by first character of namespace if !static_data.psr4.is_empty() { // Group namespaces by first character, sorted reverse - let mut by_char: BTreeMap<char, Vec<(&String, usize)>> = BTreeMap::new(); + let mut by_char: indexmap::IndexMap<_, Vec<_>> = indexmap::IndexMap::new(); let mut sorted_ns: Vec<&String> = static_data.psr4.keys().collect(); sorted_ns.sort_by(|a, b| b.cmp(a)); @@ -1278,11 +1277,11 @@ pub fn generate(config: &AutoloadConfig) -> anyhow::Result<GenerateResult> { version_normalized: p.version_normalized.clone(), source: None, dist: None, - require: std::collections::BTreeMap::new(), - require_dev: std::collections::BTreeMap::new(), - conflict: std::collections::BTreeMap::new(), - provide: std::collections::BTreeMap::new(), - replace: std::collections::BTreeMap::new(), + require: indexmap::IndexMap::new(), + require_dev: indexmap::IndexMap::new(), + conflict: indexmap::IndexMap::new(), + provide: indexmap::IndexMap::new(), + replace: indexmap::IndexMap::new(), suggest: None, package_type: p.package_type.clone(), autoload: p.autoload.clone(), @@ -1295,7 +1294,7 @@ pub fn generate(config: &AutoloadConfig) -> anyhow::Result<GenerateResult> { support: None, funding: None, time: None, - extra_fields: std::collections::BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }) .collect() }; @@ -1370,7 +1369,6 @@ pub fn generate(config: &AutoloadConfig) -> anyhow::Result<GenerateResult> { mod tests { use super::*; use crate::repository::installed::{InstalledPackageEntry, InstalledPackages}; - use std::collections::BTreeMap; use tempfile::tempdir; fn make_installed_pkg(name: &str, version: &str) -> InstalledPackageEntry { @@ -1386,7 +1384,7 @@ mod tests { aliases: vec![], homepage: None, support: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } @@ -1589,10 +1587,10 @@ mod tests { #[test] fn test_generate_autoload_psr4_empty() { let data = AutoloadData { - psr4: BTreeMap::new(), - psr0: BTreeMap::new(), - classmap: BTreeMap::new(), - files: BTreeMap::new(), + psr4: indexmap::IndexMap::new(), + psr0: indexmap::IndexMap::new(), + classmap: indexmap::IndexMap::new(), + files: indexmap::IndexMap::new(), }; let output = generate_autoload_psr4(&data); assert!(output.contains("return array(\n);")); diff --git a/crates/mozart-core/src/installer/installed_repo.rs b/crates/mozart-core/src/installer/installed_repo.rs index 8361158..d7e4f83 100644 --- a/crates/mozart-core/src/installer/installed_repo.rs +++ b/crates/mozart-core/src/installer/installed_repo.rs @@ -17,7 +17,6 @@ //! or through a `provide` / `replace` link. use indexmap::IndexSet; -use std::collections::BTreeMap; /// One installed package, in the shape `findPackagesWithReplacersAndProviders` /// needs. Mirrors the fields of `Composer\Package\PackageInterface` that the @@ -33,9 +32,9 @@ pub struct InstalledCandidate { /// Original-case version, used in user-facing output. pub pretty_version: String, /// `provide` map: target package name → constraint string. - pub provides: BTreeMap<String, String>, + pub provides: indexmap::IndexMap<String, String>, /// `replace` map: target package name → constraint string. - pub replaces: BTreeMap<String, String>, + pub replaces: indexmap::IndexMap<String, String>, } #[derive(Debug, Clone, Default)] @@ -106,8 +105,8 @@ mod tests { pretty_name: name.to_string(), version: version.to_string(), pretty_version: version.to_string(), - provides: BTreeMap::new(), - replaces: BTreeMap::new(), + provides: indexmap::IndexMap::new(), + replaces: indexmap::IndexMap::new(), } } 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<String, Vec<String>>, - pub psr0: BTreeMap<String, Vec<String>>, + pub psr4: indexmap::IndexMap<String, Vec<String>>, + pub psr0: indexmap::IndexMap<String, Vec<String>>, pub classmap: Vec<String>, pub files: Vec<String>, } @@ -125,19 +129,19 @@ pub struct PackageData { pub dist_sha1_checksum: Option<String>, pub release_date: Option<String>, - pub extra: BTreeMap<String, serde_json::Value>, + pub extra: indexmap::IndexMap<String, serde_json::Value>, pub binaries: Vec<String>, pub dev: bool, pub stability: Stability, pub notification_url: Option<String>, // dependency links - pub requires: BTreeMap<String, Link>, - pub conflicts: BTreeMap<String, Link>, - pub provides: BTreeMap<String, Link>, - pub replaces: BTreeMap<String, Link>, - pub dev_requires: BTreeMap<String, Link>, - pub suggests: BTreeMap<String, String>, + pub requires: indexmap::IndexMap<String, Link>, + pub conflicts: indexmap::IndexMap<String, Link>, + pub provides: indexmap::IndexMap<String, Link>, + pub replaces: indexmap::IndexMap<String, Link>, + pub dev_requires: indexmap::IndexMap<String, Link>, + pub suggests: indexmap::IndexMap<String, String>, // autoload pub autoload: AutoloadRules, @@ -157,7 +161,7 @@ pub struct CompletePackageData { pub license: Vec<String>, pub keywords: Vec<String>, pub authors: Vec<Author>, - pub scripts: BTreeMap<String, Vec<String>>, + pub scripts: indexmap::IndexMap<String, Vec<String>>, pub support: Support, pub funding: Vec<Funding>, pub repositories: Vec<serde_json::Value>, @@ -175,105 +179,55 @@ pub struct RootPackageData { pub minimum_stability: Stability, pub prefer_stable: bool, - pub stability_flags: BTreeMap<String, Stability>, - pub config: BTreeMap<String, serde_json::Value>, - pub references: BTreeMap<String, String>, + pub stability_flags: indexmap::IndexMap<String, Stability>, + pub config: indexmap::IndexMap<String, serde_json::Value>, + pub references: indexmap::IndexMap<String, String>, pub aliases: Vec<VersionAlias>, } -/// 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<String, serde_json::Value>; - fn binaries(&self) -> &[String]; - fn is_dev(&self) -> bool; - fn stability(&self) -> Stability; - fn notification_url(&self) -> Option<&str>; - fn requires(&self) -> &BTreeMap<String, Link>; - fn conflicts(&self) -> &BTreeMap<String, Link>; - fn provides(&self) -> &BTreeMap<String, Link>; - fn replaces(&self) -> &BTreeMap<String, Link>; - fn dev_requires(&self) -> &BTreeMap<String, Link>; - fn suggests(&self) -> &BTreeMap<String, String>; - 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<String, Vec<String>>; - 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<String, Stability>; - fn config(&self) -> &BTreeMap<String, serde_json::Value>; - fn references(&self) -> &BTreeMap<String, String>; + fn stability_flags(&self) -> &indexmap::IndexMap<String, Stability>; + fn config(&self) -> &indexmap::IndexMap<String, serde_json::Value>; + fn references(&self) -> &indexmap::IndexMap<String, String>; 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<String, serde_json::Value> { &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<String, Link> { &self.$($path).+.requires } - fn conflicts(&self) -> &BTreeMap<String, Link> { &self.$($path).+.conflicts } - fn provides(&self) -> &BTreeMap<String, Link> { &self.$($path).+.provides } - fn replaces(&self) -> &BTreeMap<String, Link> { &self.$($path).+.replaces } - fn dev_requires(&self) -> &BTreeMap<String, Link> { &self.$($path).+.dev_requires } - fn suggests(&self) -> &BTreeMap<String, String> { &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<String, serde_json::Value> { &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<String, Link> { &self.$($path).+.requires } + fn conflicts(&self) -> &indexmap::IndexMap<String, Link> { &self.$($path).+.conflicts } + fn provides(&self) -> &indexmap::IndexMap<String, Link> { &self.$($path).+.provides } + fn replaces(&self) -> &indexmap::IndexMap<String, Link> { &self.$($path).+.replaces } + fn dev_requires(&self) -> &indexmap::IndexMap<String, Link> { &self.$($path).+.dev_requires } + fn suggests(&self) -> &indexmap::IndexMap<String, String> { &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<String, Vec<String>> { &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<String, Vec<String>> { &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<String, serde_json::Value> { + fn extra(&self) -> &indexmap::IndexMap<String, serde_json::Value> { &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<String, Link> { + fn requires(&self) -> &indexmap::IndexMap<String, Link> { &self.requires } - fn conflicts(&self) -> &BTreeMap<String, Link> { + fn conflicts(&self) -> &indexmap::IndexMap<String, Link> { &self.conflicts } - fn provides(&self) -> &BTreeMap<String, Link> { + fn provides(&self) -> &indexmap::IndexMap<String, Link> { &self.provides } - fn replaces(&self) -> &BTreeMap<String, Link> { + fn replaces(&self) -> &indexmap::IndexMap<String, Link> { &self.replaces } - fn dev_requires(&self) -> &BTreeMap<String, Link> { + fn dev_requires(&self) -> &indexmap::IndexMap<String, Link> { &self.dev_requires } - fn suggests(&self) -> &BTreeMap<String, String> { + fn suggests(&self) -> &indexmap::IndexMap<String, String> { &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<String, Vec<String>> { + fn scripts(&self) -> &indexmap::IndexMap<String, Vec<String>> { &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<String, Stability> { + fn stability_flags(&self) -> &indexmap::IndexMap<String, Stability> { &self.stability_flags } - fn config(&self) -> &BTreeMap<String, serde_json::Value> { + fn config(&self) -> &indexmap::IndexMap<String, serde_json::Value> { &self.config } - fn references(&self) -> &BTreeMap<String, String> { + fn references(&self) -> &indexmap::IndexMap<String, String> { &self.references } fn aliases(&self) -> &[VersionAlias] { @@ -489,23 +443,23 @@ pub struct RawPackageData { pub minimum_stability: Option<String>, #[serde(default)] - pub require: BTreeMap<String, String>, + pub require: indexmap::IndexMap<String, String>, #[serde( rename = "require-dev", default, - skip_serializing_if = "BTreeMap::is_empty" + skip_serializing_if = "indexmap::IndexMap::is_empty" )] - pub require_dev: BTreeMap<String, String>, + pub require_dev: indexmap::IndexMap<String, String>, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub conflict: BTreeMap<String, String>, + #[serde(default, skip_serializing_if = "indexmap::IndexMap::is_empty")] + pub conflict: indexmap::IndexMap<String, String>, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub provide: BTreeMap<String, String>, + #[serde(default, skip_serializing_if = "indexmap::IndexMap::is_empty")] + pub provide: indexmap::IndexMap<String, String>, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub replace: BTreeMap<String, String>, + #[serde(default, skip_serializing_if = "indexmap::IndexMap::is_empty")] + pub replace: indexmap::IndexMap<String, String>, /// `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<String>, #[serde(flatten)] - pub extra_fields: BTreeMap<String, serde_json::Value>, + pub extra_fields: indexmap::IndexMap<String, serde_json::Value>, } #[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<String, String>, + pub psr4: indexmap::IndexMap<String, String>, } #[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<String, String>, + deps: indexmap::IndexMap<String, String>, description: &str, - ) -> BTreeMap<String, Link> { + ) -> indexmap::IndexMap<String, Link> { deps.into_iter() .map(|(target, constraint)| { let normalized = target.to_lowercase(); @@ -745,7 +699,7 @@ impl RootPackageData { }) .unwrap_or_default(); - let extra: BTreeMap<String, serde_json::Value> = raw + let extra: indexmap::IndexMap<String, serde_json::Value> = raw .extra_fields .get("extra") .and_then(|v| v.as_object()) @@ -783,7 +737,7 @@ impl RootPackageData { }) .unwrap_or_default(); - let scripts: BTreeMap<String, Vec<String>> = raw + let scripts: indexmap::IndexMap<String, Vec<String>> = raw .extra_fields .get("scripts") .and_then(|v| v.as_object()) @@ -841,7 +795,7 @@ impl RootPackageData { }) .unwrap_or_default(); - let config: BTreeMap<String, serde_json::Value> = raw + let config: indexmap::IndexMap<String, serde_json::Value> = 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 }); diff --git a/crates/mozart-core/src/package/complete_package_interface.rs b/crates/mozart-core/src/package/complete_package_interface.rs new file mode 100644 index 0000000..c321309 --- /dev/null +++ b/crates/mozart-core/src/package/complete_package_interface.rs @@ -0,0 +1,17 @@ +use crate::package::{Author, Funding, PackageInterface, Support}; + +/// ref: \Composer\Package\CompletePackageInterface +pub trait CompletePackageInterface: PackageInterface { + 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) -> &indexmap::IndexMap<String, Vec<String>>; + 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]; +} diff --git a/crates/mozart-core/src/package/package_interface.rs b/crates/mozart-core/src/package/package_interface.rs new file mode 100644 index 0000000..7006657 --- /dev/null +++ b/crates/mozart-core/src/package/package_interface.rs @@ -0,0 +1,33 @@ +use crate::package::{AutoloadRules, Link, Stability}; + +/// ref: \Composer\Package\PackageInterface +pub trait PackageInterface { + 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) -> &indexmap::IndexMap<String, serde_json::Value>; + fn binaries(&self) -> &[String]; + fn is_dev(&self) -> bool; + fn stability(&self) -> Stability; + fn notification_url(&self) -> Option<&str>; + fn requires(&self) -> &indexmap::IndexMap<String, Link>; + fn conflicts(&self) -> &indexmap::IndexMap<String, Link>; + fn provides(&self) -> &indexmap::IndexMap<String, Link>; + fn replaces(&self) -> &indexmap::IndexMap<String, Link>; + fn dev_requires(&self) -> &indexmap::IndexMap<String, Link>; + fn suggests(&self) -> &indexmap::IndexMap<String, String>; + fn autoload(&self) -> &AutoloadRules; + fn dev_autoload(&self) -> &AutoloadRules; + fn is_default_branch(&self) -> bool; +} diff --git a/crates/mozart-core/src/repository/browse_repos.rs b/crates/mozart-core/src/repository/browse_repos.rs index 83a40f1..a174637 100644 --- a/crates/mozart-core/src/repository/browse_repos.rs +++ b/crates/mozart-core/src/repository/browse_repos.rs @@ -8,8 +8,8 @@ //! `CompletePackageInterface` (`getSupport()['source']`, //! `getSourceUrl()`, `getHomepage()`). -use super::super::package::Package as _; -use super::super::package::{CompletePackage as _, RootPackageData}; +use super::super::package::PackageInterface as _; +use super::super::package::{CompletePackageInterface as _, RootPackageData}; use super::cache::Cache; use super::installed::{InstalledPackageEntry, InstalledPackages}; use super::lockfile::LockedPackage; @@ -167,7 +167,6 @@ impl BrowseRepos { mod tests { use super::*; use crate::package::RawPackageData; - use std::collections::BTreeMap; fn locked( name: &str, @@ -185,11 +184,11 @@ mod tests { reference: None, }), dist: 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(), suggest: None, package_type: None, autoload: None, @@ -202,7 +201,7 @@ mod tests { support: support_source.map(|s| serde_json::json!({"source": s})), funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } @@ -240,7 +239,7 @@ mod tests { aliases: vec![], homepage: Some("https://vendor.example.com".to_string()), support: Some(serde_json::json!({"source": "https://github.com/vendor/pkg"})), - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }; let view = CompletePackageView::from(&entry); assert_eq!( diff --git a/crates/mozart-core/src/repository/installed.rs b/crates/mozart-core/src/repository/installed.rs index 422f79f..4bee0e5 100644 --- a/crates/mozart-core/src/repository/installed.rs +++ b/crates/mozart-core/src/repository/installed.rs @@ -1,7 +1,6 @@ use crate::installer::HasSuggests; use crate::package::to_json_pretty; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; use std::fs; use std::path::Path; @@ -56,7 +55,7 @@ pub struct InstalledPackageEntry { pub support: Option<serde_json::Value>, #[serde(flatten)] - pub extra_fields: BTreeMap<String, serde_json::Value>, + pub extra_fields: indexmap::IndexMap<String, serde_json::Value>, } impl HasSuggests for InstalledPackageEntry { @@ -215,7 +214,7 @@ mod tests { aliases: vec![], homepage: None, support: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } diff --git a/crates/mozart-core/src/repository/lockfile.rs b/crates/mozart-core/src/repository/lockfile.rs index 146506d..cde7530 100644 --- a/crates/mozart-core/src/repository/lockfile.rs +++ b/crates/mozart-core/src/repository/lockfile.rs @@ -2,12 +2,12 @@ use super::packagist::{PackagistDist, PackagistSource, PackagistVersion}; use super::repository::RepositorySet; use super::resolver::ResolvedPackage; use crate::installer::HasSuggests; -use crate::package::Package as _; +use crate::package::PackageInterface as _; use crate::package::{Link, RawPackageData, to_json_pretty}; use indexmap::IndexMap; use indexmap::IndexSet; use serde::{Deserialize, Serialize}; -use std::collections::{BTreeMap, VecDeque}; +use std::collections::VecDeque; use std::fs; use std::path::Path; @@ -76,27 +76,27 @@ pub struct LockedPackage { #[serde(skip_serializing_if = "Option::is_none")] pub dist: Option<LockedDist>, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub require: BTreeMap<String, String>, + #[serde(default, skip_serializing_if = "indexmap::IndexMap::is_empty")] + pub require: indexmap::IndexMap<String, String>, #[serde( rename = "require-dev", default, - skip_serializing_if = "BTreeMap::is_empty" + skip_serializing_if = "indexmap::IndexMap::is_empty" )] - pub require_dev: BTreeMap<String, String>, + pub require_dev: indexmap::IndexMap<String, String>, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub conflict: BTreeMap<String, String>, + #[serde(default, skip_serializing_if = "indexmap::IndexMap::is_empty")] + pub conflict: indexmap::IndexMap<String, String>, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub provide: BTreeMap<String, String>, + #[serde(default, skip_serializing_if = "indexmap::IndexMap::is_empty")] + pub provide: indexmap::IndexMap<String, String>, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub replace: BTreeMap<String, String>, + #[serde(default, skip_serializing_if = "indexmap::IndexMap::is_empty")] + pub replace: indexmap::IndexMap<String, String>, #[serde(skip_serializing_if = "Option::is_none")] - pub suggest: Option<BTreeMap<String, String>>, + pub suggest: Option<indexmap::IndexMap<String, String>>, #[serde(rename = "type", skip_serializing_if = "Option::is_none")] pub package_type: Option<String>, @@ -133,7 +133,7 @@ pub struct LockedPackage { /// Catch-all for extra fields we don't explicitly model #[serde(flatten)] - pub extra_fields: BTreeMap<String, serde_json::Value>, + pub extra_fields: indexmap::IndexMap<String, serde_json::Value>, } impl HasSuggests for LockedPackage { @@ -231,9 +231,9 @@ impl LockFile { ]; // Collect relevant keys into a BTreeMap (auto-sorted by key) - let mut filtered: BTreeMap<&str, &serde_json::Value> = BTreeMap::new(); - for key in &relevant_keys { - if let Some(v) = obj.get(*key) { + let mut filtered = std::collections::BTreeMap::new(); + for key in relevant_keys { + if let Some(v) = obj.get(key) { filtered.insert(key, v); } } @@ -396,7 +396,7 @@ pub fn locked_package_branch_aliases(pkg: &LockedPackage) -> Vec<LockAlias> { } fn check_requirement_set( - requires: &BTreeMap<String, Link>, + requires: &IndexMap<String, Link>, description: &str, pool: &[LockedSearchEntry], messages: &mut Vec<String>, @@ -626,10 +626,10 @@ fn packagist_dist_to_locked(pd: &PackagistDist) -> LockedDist { /// `dev` after stripping the reference are left out (matching the /// `'dev' === VersionParser::parseStability(...)` guard in PHP). fn extract_root_references( - require: &BTreeMap<String, String>, - require_dev: &BTreeMap<String, String>, -) -> BTreeMap<String, String> { - let mut out = BTreeMap::new(); + require: &IndexMap<String, String>, + require_dev: &IndexMap<String, String>, +) -> indexmap::IndexMap<String, String> { + let mut out = indexmap::IndexMap::new(); for (name, raw_constraint) in require.iter().chain(require_dev.iter()) { if let Some(reference) = parse_inline_reference(raw_constraint) { out.insert(name.to_lowercase(), reference); @@ -730,7 +730,7 @@ fn rewrite_known_dist_url_reference(url: &str, reference: &str) -> String { /// Convert a `PackagistVersion` to a `LockedPackage`. fn packagist_version_to_locked_package(name: &str, pv: &PackagistVersion) -> LockedPackage { - let mut extra_fields: BTreeMap<String, serde_json::Value> = BTreeMap::new(); + let mut extra_fields = indexmap::IndexMap::new(); if let Some(extra) = &pv.extra { extra_fields.insert("extra".to_string(), extra.clone()); @@ -808,8 +808,8 @@ fn packagist_version_to_locked_package(name: &str, pv: &PackagistVersion) -> Loc /// declares `provide: { provided/pkg: 1.0.0 }`. fn classify_dev_packages( resolved: &[ResolvedPackage], - require: &BTreeMap<String, String>, - _require_dev: &BTreeMap<String, String>, + require: &IndexMap<String, String>, + _require_dev: &IndexMap<String, String>, requires_by_name: &IndexMap<String, Vec<String>>, providers_by_name: &IndexMap<String, Vec<String>>, ) -> IndexSet<String> { @@ -874,7 +874,7 @@ fn is_platform_name(name: &str) -> bool { /// /// Filters the map to include only platform package keys (`php`, `ext-*`, `lib-*`, etc.) /// and returns them as a JSON object. -fn extract_platform_requirements(requirements: &BTreeMap<String, String>) -> serde_json::Value { +fn extract_platform_requirements(requirements: &IndexMap<String, String>) -> serde_json::Value { let map: serde_json::Map<String, serde_json::Value> = requirements .iter() .filter(|(k, _)| is_platform_name(k)) @@ -920,12 +920,12 @@ pub async fn generate_lock_file(request: &LockFileGenerationRequest) -> anyhow:: // resolver picked a new commit at the same version label) still get // their ref refreshed. struct PreservedRelationships { - require: BTreeMap<String, String>, - require_dev: BTreeMap<String, String>, - conflict: BTreeMap<String, String>, - provide: BTreeMap<String, String>, - replace: BTreeMap<String, String>, - suggest: Option<BTreeMap<String, String>>, + require: indexmap::IndexMap<String, String>, + require_dev: indexmap::IndexMap<String, String>, + conflict: indexmap::IndexMap<String, String>, + provide: indexmap::IndexMap<String, String>, + replace: indexmap::IndexMap<String, String>, + suggest: Option<indexmap::IndexMap<String, String>>, } let mut preserved_rel: IndexMap<String, PreservedRelationships> = IndexMap::new(); if let Some(prev) = &request.previous_lock { @@ -1229,11 +1229,11 @@ mod tests { reference: Some("abc123".to_string()), shasum: Some("".to_string()), }), - 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(), suggest: None, package_type: Some("library".to_string()), autoload: None, @@ -1246,7 +1246,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }); lock.write_to_file(&path).unwrap(); @@ -1320,15 +1320,15 @@ mod tests { fn make_packagist_version( version: &str, version_normalized: &str, - require: BTreeMap<String, String>, + require: indexmap::IndexMap<String, String>, ) -> PackagistVersion { PackagistVersion { version: version.to_string(), version_normalized: version_normalized.to_string(), require, - replace: BTreeMap::new(), - provide: BTreeMap::new(), - conflict: BTreeMap::new(), + replace: indexmap::IndexMap::new(), + provide: indexmap::IndexMap::new(), + conflict: indexmap::IndexMap::new(), dist: Some(super::super::packagist::PackagistDist { dist_type: "zip".to_string(), url: format!("https://example.com/{version}.zip"), @@ -1340,7 +1340,7 @@ mod tests { url: "https://github.com/example/pkg.git".to_string(), reference: Some("deadbeef".to_string()), }), - require_dev: BTreeMap::new(), + require_dev: indexmap::IndexMap::new(), suggest: None, package_type: Some("library".to_string()), autoload: Some(serde_json::json!({"psr-4": {"Example\\": "src/"}})), @@ -1366,7 +1366,7 @@ mod tests { #[test] fn test_packagist_version_to_locked_package() { - let pv = make_packagist_version("1.2.3", "1.2.3.0", BTreeMap::new()); + let pv = make_packagist_version("1.2.3", "1.2.3.0", indexmap::IndexMap::new()); let locked = packagist_version_to_locked_package("example/pkg", &pv); assert_eq!(locked.name, "example/pkg"); @@ -1414,13 +1414,13 @@ mod tests { let pv = PackagistVersion { version: "1.0.0".to_string(), version_normalized: "1.0.0.0".to_string(), - require: BTreeMap::new(), - replace: BTreeMap::new(), - provide: BTreeMap::new(), - conflict: BTreeMap::new(), + require: indexmap::IndexMap::new(), + replace: indexmap::IndexMap::new(), + provide: indexmap::IndexMap::new(), + conflict: indexmap::IndexMap::new(), dist: None, source: None, - require_dev: BTreeMap::new(), + require_dev: indexmap::IndexMap::new(), suggest: None, package_type: None, autoload: None, @@ -1484,16 +1484,16 @@ mod tests { }, ]; - let mut require = BTreeMap::new(); + let mut require = indexmap::IndexMap::new(); require.insert("vendor/a".to_string(), "^1.0".to_string()); - let mut require_dev = BTreeMap::new(); + let mut require_dev = indexmap::IndexMap::new(); require_dev.insert("vendor/b".to_string(), "^1.0".to_string()); let mut metadata: IndexMap<String, PackagistVersion> = IndexMap::new(); // A requires C - let mut a_require = BTreeMap::new(); + let mut a_require = indexmap::IndexMap::new(); a_require.insert("vendor/c".to_string(), "^1.0".to_string()); metadata.insert( "vendor/a".to_string(), @@ -1501,7 +1501,7 @@ mod tests { ); // B requires D - let mut b_require = BTreeMap::new(); + let mut b_require = indexmap::IndexMap::new(); b_require.insert("vendor/d".to_string(), "^1.0".to_string()); metadata.insert( "vendor/b".to_string(), @@ -1511,11 +1511,11 @@ mod tests { // C and D have no deps metadata.insert( "vendor/c".to_string(), - make_packagist_version("1.0.0", "1.0.0.0", BTreeMap::new()), + make_packagist_version("1.0.0", "1.0.0.0", indexmap::IndexMap::new()), ); metadata.insert( "vendor/d".to_string(), - make_packagist_version("1.0.0", "1.0.0.0", BTreeMap::new()), + make_packagist_version("1.0.0", "1.0.0.0", indexmap::IndexMap::new()), ); let requires_by_name: IndexMap<String, Vec<String>> = metadata @@ -1577,16 +1577,16 @@ mod tests { }, ]; - let mut require = BTreeMap::new(); + let mut require = indexmap::IndexMap::new(); require.insert("vendor/a".to_string(), "^1.0".to_string()); - let mut require_dev = BTreeMap::new(); + let mut require_dev = indexmap::IndexMap::new(); require_dev.insert("vendor/b".to_string(), "^1.0".to_string()); let mut metadata: IndexMap<String, PackagistVersion> = IndexMap::new(); // A requires C - let mut a_require = BTreeMap::new(); + let mut a_require = indexmap::IndexMap::new(); a_require.insert("vendor/c".to_string(), "^1.0".to_string()); metadata.insert( "vendor/a".to_string(), @@ -1594,7 +1594,7 @@ mod tests { ); // B also requires C - let mut b_require = BTreeMap::new(); + let mut b_require = indexmap::IndexMap::new(); b_require.insert("vendor/c".to_string(), "^1.0".to_string()); metadata.insert( "vendor/b".to_string(), @@ -1604,7 +1604,7 @@ mod tests { // C has no deps metadata.insert( "vendor/c".to_string(), - make_packagist_version("1.0.0", "1.0.0.0", BTreeMap::new()), + make_packagist_version("1.0.0", "1.0.0.0", indexmap::IndexMap::new()), ); let requires_by_name: IndexMap<String, Vec<String>> = metadata @@ -1636,7 +1636,7 @@ mod tests { #[test] fn test_extract_platform_requirements() { - let mut requirements = BTreeMap::new(); + let mut requirements = indexmap::IndexMap::new(); requirements.insert("php".to_string(), ">=8.1".to_string()); requirements.insert("ext-json".to_string(), "*".to_string()); requirements.insert("ext-mbstring".to_string(), "*".to_string()); @@ -1669,7 +1669,7 @@ mod tests { #[test] fn test_extract_platform_requirements_empty() { - let requirements = BTreeMap::new(); + let requirements = indexmap::IndexMap::new(); let platform = extract_platform_requirements(&requirements); assert_eq!(platform, serde_json::json!({})); } @@ -1729,11 +1729,11 @@ mod tests { version_normalized: None, source: None, dist: 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(), suggest: None, package_type: None, autoload: None, @@ -1746,7 +1746,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }, LockedPackage { name: "vendor/alpha".to_string(), @@ -1754,11 +1754,11 @@ mod tests { version_normalized: None, source: None, dist: 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(), suggest: None, package_type: None, autoload: None, @@ -1771,7 +1771,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }, ]; @@ -1892,11 +1892,11 @@ mod tests { version_normalized: None, source: None, dist: 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(), suggest: None, package_type: Some("library".to_string()), autoload: None, @@ -1909,7 +1909,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } diff --git a/crates/mozart-core/src/repository/packagist.rs b/crates/mozart-core/src/repository/packagist.rs index 199ff51..91b47df 100644 --- a/crates/mozart-core/src/repository/packagist.rs +++ b/crates/mozart-core/src/repository/packagist.rs @@ -55,13 +55,13 @@ pub struct PackagistVersion { pub version: String, pub version_normalized: String, #[serde(default, deserialize_with = "deserialize_unset_as_default")] - pub require: BTreeMap<String, String>, + pub require: indexmap::IndexMap<String, String>, #[serde(default, deserialize_with = "deserialize_unset_as_default")] - pub replace: BTreeMap<String, String>, + pub replace: indexmap::IndexMap<String, String>, #[serde(default, deserialize_with = "deserialize_unset_as_default")] - pub provide: BTreeMap<String, String>, + pub provide: indexmap::IndexMap<String, String>, #[serde(default, deserialize_with = "deserialize_unset_as_default")] - pub conflict: BTreeMap<String, String>, + pub conflict: indexmap::IndexMap<String, String>, #[serde(default, deserialize_with = "deserialize_unset_as_none")] pub dist: Option<PackagistDist>, #[serde(default, deserialize_with = "deserialize_unset_as_none")] @@ -72,10 +72,10 @@ pub struct PackagistVersion { default, deserialize_with = "deserialize_unset_as_default" )] - pub require_dev: BTreeMap<String, String>, + pub require_dev: indexmap::IndexMap<String, String>, #[serde(default, deserialize_with = "deserialize_unset_as_none")] - pub suggest: Option<BTreeMap<String, String>>, + pub suggest: Option<indexmap::IndexMap<String, String>>, #[serde( rename = "type", @@ -160,17 +160,17 @@ impl PackagistVersion { /// /// Returns a map from branch name (e.g. `"dev-master"`) to alias target /// (e.g. `"2.x-dev"`). Returns an empty map when no aliases are declared. - pub fn branch_aliases(&self) -> BTreeMap<String, String> { + pub fn branch_aliases(&self) -> indexmap::IndexMap<String, String> { let Some(extra) = &self.extra else { - return BTreeMap::new(); + return indexmap::IndexMap::new(); }; let Some(branch_alias) = extra.get("branch-alias") else { - return BTreeMap::new(); + return indexmap::IndexMap::new(); }; let Some(map) = branch_alias.as_object() else { - return BTreeMap::new(); + return indexmap::IndexMap::new(); }; map.iter() diff --git a/crates/mozart-core/src/repository/vcs_bridge.rs b/crates/mozart-core/src/repository/vcs_bridge.rs index 18a8420..79c0c5b 100644 --- a/crates/mozart-core/src/repository/vcs_bridge.rs +++ b/crates/mozart-core/src/repository/vcs_bridge.rs @@ -10,7 +10,6 @@ use crate::repository::resolver::{parse_normalized, version_stability}; use crate::repository::vcs::DriverConfig; use crate::vcs::repository::{VcsPackageVersion, VcsRepository}; use indexmap::IndexMap; -use std::collections::BTreeMap; /// Scan all VCS-type repositories and collect package versions. /// @@ -191,7 +190,7 @@ pub fn vcs_to_packagist_version(vpkg: &VcsPackageVersion) -> PackagistVersion { } /// Extract a dependency map from composer.json JSON. -fn extract_dep_map(json: &serde_json::Value, key: &str) -> BTreeMap<String, String> { +fn extract_dep_map(json: &serde_json::Value, key: &str) -> indexmap::IndexMap<String, String> { json.get(key) .and_then(|v| v.as_object()) .map(|obj| { diff --git a/crates/mozart-core/src/repository_utils.rs b/crates/mozart-core/src/repository_utils.rs index b16a0d6..7a797c4 100644 --- a/crates/mozart-core/src/repository_utils.rs +++ b/crates/mozart-core/src/repository_utils.rs @@ -19,7 +19,7 @@ use std::collections::BTreeSet; /// `package_name` only in that case. pub trait Required { fn package_name(&self) -> &str; - fn requires(&self) -> &std::collections::BTreeMap<String, String>; + fn requires(&self) -> &indexmap::IndexMap<String, String>; fn package_names(&self) -> Option<Vec<&str>> { None } @@ -38,8 +38,8 @@ pub trait Required { /// discovered, matching PHP's `$bucket[] = $candidate;` push pattern. pub fn filter_required_packages<P, V>( packages: &[P], - requirer_requires: &std::collections::BTreeMap<String, V>, - requirer_dev_requires: Option<&std::collections::BTreeMap<String, V>>, + requirer_requires: &indexmap::IndexMap<String, V>, + requirer_dev_requires: Option<&indexmap::IndexMap<String, V>>, ) -> Vec<usize> where P: Required, @@ -78,24 +78,23 @@ where #[cfg(test)] mod tests { use super::*; - use std::collections::BTreeMap; struct Pkg { name: String, - requires: BTreeMap<String, String>, + requires: indexmap::IndexMap<String, String>, } impl Required for Pkg { fn package_name(&self) -> &str { &self.name } - fn requires(&self) -> &BTreeMap<String, String> { + fn requires(&self) -> &indexmap::IndexMap<String, String> { &self.requires } } fn pkg(name: &str, requires: &[&str]) -> Pkg { - let mut r = BTreeMap::new(); + let mut r = indexmap::IndexMap::new(); for n in requires { r.insert(n.to_string(), "*".to_string()); } @@ -105,8 +104,8 @@ mod tests { } } - fn root_requires(names: &[&str]) -> BTreeMap<String, String> { - let mut m = BTreeMap::new(); + fn root_requires(names: &[&str]) -> indexmap::IndexMap<String, String> { + let mut m = indexmap::IndexMap::new(); for n in names { m.insert(n.to_string(), "*".to_string()); } @@ -167,7 +166,7 @@ mod tests { #[test] fn empty_requires_yields_nothing() { let packages = vec![pkg("a/a", &[]), pkg("b/b", &[])]; - let root: BTreeMap<String, String> = BTreeMap::new(); + let root: indexmap::IndexMap<_, ()> = indexmap::IndexMap::new(); let kept = filter_required_packages(&packages, &root, None); assert!(kept.is_empty()); } diff --git a/crates/mozart/src/commands/audit.rs b/crates/mozart/src/commands/audit.rs index 0b84ccf..f543cb4 100644 --- a/crates/mozart/src/commands/audit.rs +++ b/crates/mozart/src/commands/audit.rs @@ -194,8 +194,6 @@ fn load_locked_packages(working_dir: &Path, no_dev: bool) -> anyhow::Result<Vec< #[cfg(test)] mod tests { - use std::collections::BTreeMap; - use super::*; use mozart_core::repository::lockfile::{LockFile, LockedPackage}; @@ -242,7 +240,7 @@ mod tests { aliases: vec![], homepage: None, support: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }); installed.write(&vendor_dir).unwrap(); @@ -273,7 +271,7 @@ mod tests { aliases: vec![], homepage: None, support: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }); installed.upsert(mozart_core::repository::installed::InstalledPackageEntry { name: "phpunit/phpunit".to_string(), @@ -287,7 +285,7 @@ mod tests { aliases: vec![], homepage: None, support: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }); installed .dev_package_names @@ -315,11 +313,11 @@ mod tests { version_normalized: Some("3.0.0.0".to_string()), source: None, dist: 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(), suggest: None, package_type: None, autoload: None, @@ -332,7 +330,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }], packages_dev: None, aliases: vec![], @@ -370,11 +368,11 @@ mod tests { version_normalized: None, source: None, dist: 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(), suggest: None, package_type: None, autoload: None, @@ -387,7 +385,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }], packages_dev: Some(vec![LockedPackage { name: "phpunit/phpunit".to_string(), @@ -395,11 +393,11 @@ mod tests { version_normalized: None, source: None, dist: 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(), suggest: None, package_type: None, autoload: None, @@ -412,7 +410,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }]), aliases: vec![], minimum_stability: "stable".to_string(), diff --git a/crates/mozart/src/commands/browse.rs b/crates/mozart/src/commands/browse.rs index e3eae22..957f4bc 100644 --- a/crates/mozart/src/commands/browse.rs +++ b/crates/mozart/src/commands/browse.rs @@ -4,7 +4,7 @@ use mozart_core::console::IoInterface; use mozart_core::console_writeln; use mozart_core::console_writeln_error; use mozart_core::exit_code; -use mozart_core::package::Package as _; +use mozart_core::package::PackageInterface as _; use mozart_core::repository::browse_repos::{BrowseRepos, CompletePackageView}; use mozart_core::repository::cache::{Cache, build_cache_config}; use mozart_core::repository::installed::InstalledPackages; diff --git a/crates/mozart/src/commands/bump.rs b/crates/mozart/src/commands/bump.rs index 351be01..2d6eea8 100644 --- a/crates/mozart/src/commands/bump.rs +++ b/crates/mozart/src/commands/bump.rs @@ -3,7 +3,7 @@ use clap::Args; use indexmap::IndexMap; use mozart_core::composer::LocalRepository; use mozart_core::console::IoInterface; -use mozart_core::package::{Link, Package as _}; +use mozart_core::package::PackageInterface as _; use mozart_core::{console_writeln, console_writeln_error}; use std::collections::BTreeMap; use std::path::Path; @@ -151,7 +151,7 @@ pub async fn do_bump( } } - let mut tasks: Vec<(&'static str, &BTreeMap<String, Link>)> = Vec::new(); + let mut tasks = Vec::new(); if !dev_only { tasks.push(("require", composer.package().requires())); } @@ -386,11 +386,11 @@ mod tests { version_normalized: Some(format!("{version}.0")), source: None, dist: 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(), suggest: None, package_type: None, autoload: None, @@ -403,7 +403,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } diff --git a/crates/mozart/src/commands/check_platform_reqs.rs b/crates/mozart/src/commands/check_platform_reqs.rs index 2dbcd3b..f6731d7 100644 --- a/crates/mozart/src/commands/check_platform_reqs.rs +++ b/crates/mozart/src/commands/check_platform_reqs.rs @@ -3,7 +3,6 @@ use mozart_core::console::IoInterface; use mozart_core::console_writeln; use mozart_core::console_writeln_error; use mozart_core::installer::{InstalledCandidate, InstalledRepoLite}; -use std::collections::BTreeMap; use std::path::Path; #[derive(Args)] @@ -75,7 +74,7 @@ pub async fn execute( let installed_path = vendor_dir.join("composer/installed.json"); let mut installed_repo = InstalledRepoLite::new(); - let mut requires: BTreeMap<String, Vec<Link>> = BTreeMap::new(); + let mut requires = indexmap::IndexMap::new(); if args.lock { if !lock_path.exists() { @@ -141,8 +140,8 @@ pub async fn execute( pretty_name: pkg.name, version: pkg.version.clone(), pretty_version: pkg.version, - provides: BTreeMap::new(), - replaces: BTreeMap::new(), + provides: indexmap::IndexMap::new(), + replaces: indexmap::IndexMap::new(), }); } @@ -251,7 +250,7 @@ fn load_lock( lock_path: &Path, no_dev: bool, repo: &mut InstalledRepoLite, - requires: &mut BTreeMap<String, Vec<Link>>, + requires: &mut indexmap::IndexMap<String, Vec<Link>>, ) -> anyhow::Result<()> { let lock = mozart_core::repository::lockfile::LockFile::read_from_file(lock_path)?; @@ -282,7 +281,7 @@ fn load_installed( installed: &mozart_core::repository::installed::InstalledPackages, no_dev: bool, repo: &mut InstalledRepoLite, - requires: &mut BTreeMap<String, Vec<Link>>, + requires: &mut indexmap::IndexMap<String, Vec<Link>>, ) { let dev_names: indexmap::IndexSet<String> = installed .dev_package_names @@ -334,10 +333,10 @@ fn add_root_as_candidate( } fn string_map_from_extra( - extra: &BTreeMap<String, serde_json::Value>, + extra: &indexmap::IndexMap<String, serde_json::Value>, key: &str, -) -> BTreeMap<String, String> { - let mut out: BTreeMap<String, String> = BTreeMap::new(); +) -> indexmap::IndexMap<String, String> { + let mut out = indexmap::IndexMap::new(); if let Some(val) = extra.get(key) && let Some(obj) = val.as_object() { @@ -351,7 +350,7 @@ fn string_map_from_extra( } fn push_platform_link( - requires: &mut BTreeMap<String, Vec<Link>>, + requires: &mut indexmap::IndexMap<String, Vec<Link>>, source: &str, target: &str, constraint: &str, @@ -470,7 +469,6 @@ fn print_table( mod tests { use super::*; use mozart_core::console::Console; - use std::collections::BTreeMap; use tempfile::tempdir; fn test_console() -> std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>> { @@ -481,22 +479,26 @@ mod tests { fn write_lock( path: &Path, - packages: &[(&str, BTreeMap<String, String>)], - dev_packages: &[(&str, BTreeMap<String, String>)], + packages: &[(&str, indexmap::IndexMap<String, String>)], + dev_packages: &[(&str, indexmap::IndexMap<String, String>)], ) { write_lock_with(path, packages, dev_packages, &[]); } fn write_lock_with( path: &Path, - packages: &[(&str, BTreeMap<String, String>)], - dev_packages: &[(&str, BTreeMap<String, String>)], - provides: &[(&str, BTreeMap<String, String>, BTreeMap<String, String>)], // (name, provide, replace) + packages: &[(&str, indexmap::IndexMap<String, String>)], + dev_packages: &[(&str, indexmap::IndexMap<String, String>)], + provides: &[( + &str, + indexmap::IndexMap<String, String>, + indexmap::IndexMap<String, String>, + )], // (name, provide, replace) ) { let make_pkg = |name: &str, - require: BTreeMap<String, String>, - provide: BTreeMap<String, String>, - replace: BTreeMap<String, String>| { + require: indexmap::IndexMap<String, String>, + provide: indexmap::IndexMap<String, String>, + replace: indexmap::IndexMap<String, String>| { serde_json::json!({ "name": name, "version": "1.0.0", @@ -508,15 +510,34 @@ mod tests { let mut pkgs_json: Vec<serde_json::Value> = packages .iter() - .map(|(name, req)| make_pkg(name, req.clone(), BTreeMap::new(), BTreeMap::new())) + .map(|(name, req)| { + make_pkg( + name, + req.clone(), + indexmap::IndexMap::new(), + indexmap::IndexMap::new(), + ) + }) .collect(); for (name, prov, repl) in provides { - pkgs_json.push(make_pkg(name, BTreeMap::new(), prov.clone(), repl.clone())); + pkgs_json.push(make_pkg( + name, + indexmap::IndexMap::new(), + prov.clone(), + repl.clone(), + )); } let dev_pkgs_json: Vec<serde_json::Value> = dev_packages .iter() - .map(|(name, req)| make_pkg(name, req.clone(), BTreeMap::new(), BTreeMap::new())) + .map(|(name, req)| { + make_pkg( + name, + req.clone(), + indexmap::IndexMap::new(), + indexmap::IndexMap::new(), + ) + }) .collect(); let lock_json = serde_json::json!({ @@ -565,7 +586,7 @@ mod tests { let dir = tempdir().unwrap(); let lock_path = dir.path().join("composer.lock"); - let mut pkg_require = BTreeMap::new(); + let mut pkg_require = indexmap::IndexMap::new(); pkg_require.insert("php".to_string(), ">=8.1".to_string()); pkg_require.insert("ext-json".to_string(), "*".to_string()); pkg_require.insert("monolog/monolog".to_string(), "^3.0".to_string()); // not platform @@ -573,7 +594,7 @@ mod tests { write_lock(&lock_path, &[("vendor/pkg", pkg_require)], &[]); let mut repo = InstalledRepoLite::new(); - let mut requires: BTreeMap<String, Vec<Link>> = BTreeMap::new(); + let mut requires = indexmap::IndexMap::new(); load_lock(&lock_path, false, &mut repo, &mut requires).unwrap(); assert!(requires.contains_key("php")); @@ -591,10 +612,10 @@ mod tests { let dir = tempdir().unwrap(); let lock_path = dir.path().join("composer.lock"); - let mut prod_require = BTreeMap::new(); + let mut prod_require = indexmap::IndexMap::new(); prod_require.insert("php".to_string(), ">=8.0".to_string()); - let mut dev_require = BTreeMap::new(); + let mut dev_require = indexmap::IndexMap::new(); dev_require.insert("ext-xdebug".to_string(), "*".to_string()); write_lock( @@ -604,13 +625,13 @@ mod tests { ); let mut repo = InstalledRepoLite::new(); - let mut requires: BTreeMap<String, Vec<Link>> = BTreeMap::new(); + let mut requires = indexmap::IndexMap::new(); load_lock(&lock_path, true, &mut repo, &mut requires).unwrap(); assert!(requires.contains_key("php")); assert!(!requires.contains_key("ext-xdebug")); let mut repo2 = InstalledRepoLite::new(); - let mut requires2: BTreeMap<String, Vec<Link>> = BTreeMap::new(); + let mut requires2 = indexmap::IndexMap::new(); load_lock(&lock_path, false, &mut repo2, &mut requires2).unwrap(); assert!(requires2.contains_key("ext-xdebug")); } @@ -626,10 +647,10 @@ mod tests { pretty_name: "vendor/pkg".into(), version: "1.0.0".into(), pretty_version: "1.0.0".into(), - provides: BTreeMap::new(), - replaces: BTreeMap::new(), + provides: indexmap::IndexMap::new(), + replaces: indexmap::IndexMap::new(), }); - let mut polyfill_provides = BTreeMap::new(); + let mut polyfill_provides = indexmap::IndexMap::new(); polyfill_provides.insert("ext-mbstring".to_string(), "*".to_string()); repo.add_candidate(InstalledCandidate { name: "symfony/polyfill-mbstring".into(), @@ -637,7 +658,7 @@ mod tests { version: "1.30.0".into(), pretty_version: "1.30.0".into(), provides: polyfill_provides, - replaces: BTreeMap::new(), + replaces: indexmap::IndexMap::new(), }); let candidates = repo.find_with_replacers_and_providers("ext-mbstring"); @@ -652,7 +673,7 @@ mod tests { #[test] fn test_replacer_candidate_satisfies_require() { - let mut replaces = BTreeMap::new(); + let mut replaces = indexmap::IndexMap::new(); replaces.insert("ext-mbstring".to_string(), "1.0".to_string()); let mut repo = InstalledRepoLite::new(); @@ -661,7 +682,7 @@ mod tests { pretty_name: "vendor/legacy-replacement".into(), version: "2.0.0".into(), pretty_version: "2.0.0".into(), - provides: BTreeMap::new(), + provides: indexmap::IndexMap::new(), replaces, }); diff --git a/crates/mozart/src/commands/dependency.rs b/crates/mozart/src/commands/dependency.rs index 70d1644..5766b08 100644 --- a/crates/mozart/src/commands/dependency.rs +++ b/crates/mozart/src/commands/dependency.rs @@ -9,7 +9,6 @@ use indexmap::IndexSet; use mozart_core::console::IoInterface; use mozart_core::console_format; use mozart_core::console_writeln; -use std::collections::BTreeMap; use std::path::Path; /// Inputs for [`do_execute`], collected from the `depends` / `prohibits` CLI args. @@ -160,11 +159,11 @@ pub struct PackageInfo { pub name: String, pub version: String, /// Runtime requirements (`require` section). - pub require: BTreeMap<String, String>, + pub require: indexmap::IndexMap<String, String>, /// Dev requirements (`require-dev`) — only non-empty for the root package. - pub require_dev: BTreeMap<String, String>, + pub require_dev: indexmap::IndexMap<String, String>, /// Conflict declarations (`conflict` section). - pub conflict: BTreeMap<String, String>, + pub conflict: indexmap::IndexMap<String, String>, /// Whether this is the root `composer.json` package. pub is_root: bool, } @@ -218,9 +217,9 @@ pub fn load_packages(working_dir: &Path, locked: bool) -> Result<Vec<PackageInfo packages.push(PackageInfo { name: pp.name.clone(), version: pp.version.clone(), - require: BTreeMap::new(), - require_dev: BTreeMap::new(), - conflict: BTreeMap::new(), + require: indexmap::IndexMap::new(), + require_dev: indexmap::IndexMap::new(), + conflict: indexmap::IndexMap::new(), is_root: false, }); } @@ -230,7 +229,7 @@ pub fn load_packages(working_dir: &Path, locked: bool) -> Result<Vec<PackageInfo && let Ok(root) = mozart_core::package::read_from_file(&composer_json_path) { // Extract conflict from extra_fields if present - let conflict: BTreeMap<String, String> = root + let conflict = root .extra_fields .get("conflict") .and_then(|v| v.as_object()) @@ -267,7 +266,7 @@ fn load_from_lockfile(lock_path: &Path) -> Result<Vec<PackageInfo>> { name: pkg.name.clone(), version: pkg.version.clone(), require: pkg.require.clone(), - require_dev: BTreeMap::new(), // locked packages don't expose require-dev + require_dev: indexmap::IndexMap::new(), // locked packages don't expose require-dev conflict: pkg.conflict.clone(), is_root: false, }); @@ -279,7 +278,7 @@ fn load_from_lockfile(lock_path: &Path) -> Result<Vec<PackageInfo>> { name: pkg.name.clone(), version: pkg.version.clone(), require: pkg.require.clone(), - require_dev: BTreeMap::new(), + require_dev: indexmap::IndexMap::new(), conflict: pkg.conflict.clone(), is_root: false, }); @@ -325,7 +324,7 @@ fn load_from_installed(working_dir: &Path) -> Result<Vec<PackageInfo>> { name: p.name.clone(), version: p.version.clone(), require, - require_dev: BTreeMap::new(), + require_dev: indexmap::IndexMap::new(), conflict, is_root: false, } @@ -367,7 +366,7 @@ fn get_dependents_forward( let needle_set: IndexSet<String> = needles.iter().map(|n| n.to_lowercase()).collect(); // Build name→PackageInfo lookup - let pkg_map: BTreeMap<String, &PackageInfo> = packages + let pkg_map: indexmap::IndexMap<_, _> = packages .iter() .map(|p| (p.name.to_lowercase(), p)) .collect(); @@ -448,7 +447,7 @@ fn collect_direct_requires(packages: &[PackageInfo], needle: &str) -> Vec<Depend fn recurse_dependents( packages: &[PackageInfo], needle: &str, - pkg_map: &BTreeMap<String, &PackageInfo>, + pkg_map: &indexmap::IndexMap<String, &PackageInfo>, visited: &mut IndexSet<String>, _original_needles: &IndexSet<String>, ) -> Vec<DependencyResult> { @@ -760,7 +759,7 @@ mod tests { .iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(), - require_dev: BTreeMap::new(), + require_dev: indexmap::IndexMap::new(), conflict: conflict .iter() .map(|(k, v)| (k.to_string(), v.to_string())) diff --git a/crates/mozart/src/commands/diagnose.rs b/crates/mozart/src/commands/diagnose.rs index a1d655f..6b2817a 100644 --- a/crates/mozart/src/commands/diagnose.rs +++ b/crates/mozart/src/commands/diagnose.rs @@ -8,7 +8,7 @@ use mozart_core::console::IoInterface; use mozart_core::console_writeln; use mozart_core::factory::create_config; use mozart_core::http::HttpDownloader; -use mozart_core::package::CompletePackage as _; +use mozart_core::package::CompletePackageInterface as _; use std::borrow::Cow; use std::path::Path; diff --git a/crates/mozart/src/commands/exec.rs b/crates/mozart/src/commands/exec.rs index f2a9c55..1078158 100644 --- a/crates/mozart/src/commands/exec.rs +++ b/crates/mozart/src/commands/exec.rs @@ -1,6 +1,6 @@ use crate::composer::Composer; use clap::Args; -use mozart_core::package::Package as _; +use mozart_core::package::PackageInterface as _; use mozart_core::{console::IoInterface, console_writeln}; use std::path::{Path, PathBuf}; diff --git a/crates/mozart/src/commands/init.rs b/crates/mozart/src/commands/init.rs index 0e16054..4598d39 100644 --- a/crates/mozart/src/commands/init.rs +++ b/crates/mozart/src/commands/init.rs @@ -425,7 +425,7 @@ async fn build_interactive( "Would you like to define your dev dependencies (require-dev) interactively [<comment>yes</comment>]?" )) { - let all_required: BTreeMap<String, String> = require + let all_required: indexmap::IndexMap<_, _> = require .iter() .chain(require_dev.iter()) .map(|(k, v)| (k.clone(), v.clone())) @@ -493,7 +493,7 @@ async fn build_interactive( /// Returns a map of package name → version constraint selected by the user. async fn interactive_search_packages( label: &str, - already_required: &BTreeMap<String, String>, + already_required: &indexmap::IndexMap<String, String>, preferred_stability: Stability, repo_cache: &mozart_core::repository::cache::Cache, io: &std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, @@ -749,8 +749,8 @@ fn get_git_config_value(key: &str) -> Option<String> { get_git_config().get(key).cloned().filter(|v| !v.is_empty()) } -fn parse_requirements(reqs: &[String]) -> anyhow::Result<BTreeMap<String, String>> { - let mut map = BTreeMap::new(); +fn parse_requirements(reqs: &[String]) -> anyhow::Result<indexmap::IndexMap<String, String>> { + let mut map = indexmap::IndexMap::new(); for req in reqs { let (name, version) = validation::parse_require_string(req).map_err(|e| anyhow::anyhow!(e))?; @@ -761,7 +761,7 @@ fn parse_requirements(reqs: &[String]) -> anyhow::Result<BTreeMap<String, String fn build_autoload(path: &str, package_name: &str) -> Option<RawAutoload> { let namespace = validation::namespace_from_package_name(package_name)?; - let mut psr4 = BTreeMap::new(); + let mut psr4 = indexmap::IndexMap::new(); psr4.insert(format!("{namespace}\\"), path.to_string()); Some(RawAutoload { psr4 }) } diff --git a/crates/mozart/src/commands/install.rs b/crates/mozart/src/commands/install.rs index 54256e2..709b934 100644 --- a/crates/mozart/src/commands/install.rs +++ b/crates/mozart/src/commands/install.rs @@ -2,7 +2,7 @@ use clap::Args; use indexmap::IndexSet; use mozart_core::console::IoInterface; use mozart_core::console_format; -use mozart_core::package::{Package as _, RootPackage as _, RootPackageData}; +use mozart_core::package::{PackageInterface as _, RootPackage as _, RootPackageData}; use mozart_core::repository::installed; use mozart_core::repository::installer_executor::{ Action, ExecuteContext, FilesystemExecutor, InstallerExecutor, PackageOperation, @@ -11,7 +11,6 @@ use mozart_core::repository::installer_executor::{ locked_to_installed_entry, previously_installed_alias_versions, }; use mozart_core::repository::lockfile; -use std::collections::BTreeMap; use std::path::Path; #[derive(Args)] @@ -212,7 +211,7 @@ fn verify_lock_platform_problems( /// `provide` is intentionally excluded — `getNames(false)` excludes it, and /// virtual `provide` targets allow multiple co-installed providers. fn verify_lock_same_name_problems(lock: &lockfile::LockFile, dev_mode: bool) -> Vec<String> { - let mut providers: BTreeMap<String, Vec<String>> = BTreeMap::new(); + let mut providers: indexmap::IndexMap<_, Vec<_>> = indexmap::IndexMap::new(); let mut all_pkgs: Vec<&lockfile::LockedPackage> = lock.packages.iter().collect(); if dev_mode { @@ -397,8 +396,8 @@ fn combine_platform_requirements( root: &RootPackageData, lock: &lockfile::LockFile, dev_mode: bool, -) -> BTreeMap<String, String> { - let mut combined: BTreeMap<String, String> = BTreeMap::new(); +) -> indexmap::IndexMap<String, String> { + let mut combined = indexmap::IndexMap::new(); if let Some(obj) = lock.platform.as_object() { for (name, val) in obj { @@ -434,7 +433,7 @@ fn combine_platform_requirements( } fn check_platform_requirements_against( - combined: &BTreeMap<String, String>, + combined: &indexmap::IndexMap<String, String>, platform: &[mozart_core::platform::PlatformPackage], ignore_platform_reqs: bool, ignore_platform_req: &[String], @@ -1081,7 +1080,6 @@ pub async fn run( #[cfg(test)] mod tests { use super::*; - use std::collections::BTreeMap; use tempfile::tempdir; fn make_locked_package(name: &str, version: &str) -> lockfile::LockedPackage { @@ -1091,11 +1089,11 @@ mod tests { version_normalized: None, source: None, dist: 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(), suggest: None, package_type: Some("library".to_string()), autoload: None, @@ -1108,7 +1106,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } @@ -1125,7 +1123,7 @@ mod tests { aliases: vec![], homepage: None, support: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } @@ -1506,9 +1504,10 @@ mod tests { #[test] fn check_platform_requirements_reports_missing_extension() { - let combined: BTreeMap<String, String> = [("ext-foo".to_string(), "^10".to_string())] - .into_iter() - .collect(); + let combined: indexmap::IndexMap<String, String> = + [("ext-foo".to_string(), "^10".to_string())] + .into_iter() + .collect(); let platform = vec![pp("php", "8.2.0")]; let problems = check_platform_requirements_against(&combined, &platform, false, &[]); @@ -1521,7 +1520,7 @@ mod tests { #[test] fn check_platform_requirements_reports_unsatisfied_php() { - let combined: BTreeMap<String, String> = [("php".to_string(), "^20".to_string())] + let combined: indexmap::IndexMap<String, String> = [("php".to_string(), "^20".to_string())] .into_iter() .collect(); let platform = vec![pp("php", "8.2.0")]; @@ -1536,9 +1535,10 @@ mod tests { #[test] fn check_platform_requirements_satisfied_returns_empty() { - let combined: BTreeMap<String, String> = [("php".to_string(), "^8.0".to_string())] - .into_iter() - .collect(); + let combined: indexmap::IndexMap<String, String> = + [("php".to_string(), "^8.0".to_string())] + .into_iter() + .collect(); let platform = vec![pp("php", "8.2.0")]; let problems = check_platform_requirements_against(&combined, &platform, false, &[]); @@ -1547,9 +1547,10 @@ mod tests { #[test] fn check_platform_requirements_ignore_platform_reqs_short_circuits() { - let combined: BTreeMap<String, String> = [("ext-foo".to_string(), "^10".to_string())] - .into_iter() - .collect(); + let combined: indexmap::IndexMap<String, String> = + [("ext-foo".to_string(), "^10".to_string())] + .into_iter() + .collect(); let platform: Vec<mozart_core::platform::PlatformPackage> = vec![]; let problems = check_platform_requirements_against(&combined, &platform, true, &[]); @@ -1558,7 +1559,7 @@ mod tests { #[test] fn check_platform_requirements_specific_ignore_filters_named_packages() { - let combined: BTreeMap<String, String> = [ + let combined: indexmap::IndexMap<String, String> = [ ("ext-foo".to_string(), "^10".to_string()), ("ext-bar".to_string(), "^10".to_string()), ] diff --git a/crates/mozart/src/commands/licenses.rs b/crates/mozart/src/commands/licenses.rs index 73f2018..0e4ea0b 100644 --- a/crates/mozart/src/commands/licenses.rs +++ b/crates/mozart/src/commands/licenses.rs @@ -4,14 +4,13 @@ use indexmap::IndexMap; use mozart_core::console::IoInterface; use mozart_core::console::hyperlink; use mozart_core::console_writeln; -use mozart_core::package::Package as _; +use mozart_core::package::PackageInterface as _; use mozart_core::package_info; use mozart_core::package_info::PackageUrls; use mozart_core::package_sorter::sort_packages_alphabetically; use mozart_core::repository_utils; use mozart_core::repository_utils::Required; use serde::Serialize as _; -use std::collections::BTreeMap; #[derive(Args)] pub struct LicensesArgs { @@ -38,7 +37,7 @@ struct LicenseEntry { name: String, version: String, licenses: Vec<String>, - requires: BTreeMap<String, String>, + requires: indexmap::IndexMap<String, String>, support_source: Option<String>, source_url: Option<String>, homepage: Option<String>, @@ -48,7 +47,7 @@ impl Required for LicenseEntry { fn package_name(&self) -> &str { &self.name } - fn requires(&self) -> &BTreeMap<String, String> { + fn requires(&self) -> &indexmap::IndexMap<String, String> { &self.requires } } @@ -144,7 +143,7 @@ fn read_root_licenses(composer_json_path: &std::path::Path) -> anyhow::Result<Ve fn load_installed_entries<V>( working_dir: &std::path::Path, - root_requires: &BTreeMap<String, V>, + root_requires: &indexmap::IndexMap<String, V>, no_dev: bool, ) -> anyhow::Result<Vec<LicenseEntry>> { let vendor_dir = working_dir.join("vendor"); @@ -453,7 +452,6 @@ fn tally_licenses(entries: &[LicenseEntry]) -> Vec<(String, usize)> { #[cfg(test)] mod tests { use super::*; - use std::collections::BTreeMap; fn entry(name: &str, licenses: &[&str]) -> LicenseEntry { LicenseEntry { @@ -461,7 +459,7 @@ mod tests { name: name.to_lowercase(), version: "1.0.0".to_string(), licenses: licenses.iter().map(|s| s.to_string()).collect(), - requires: BTreeMap::new(), + requires: indexmap::IndexMap::new(), support_source: None, source_url: None, homepage: None, @@ -547,7 +545,7 @@ mod tests { #[test] fn installed_to_entry_extracts_require_and_license() { use mozart_core::repository::installed::InstalledPackageEntry; - let mut extra = BTreeMap::new(); + let mut extra = indexmap::IndexMap::new(); extra.insert("license".to_string(), serde_json::json!(["MIT"])); extra.insert( "require".to_string(), @@ -587,7 +585,7 @@ mod tests { aliases: vec![], homepage: Some("https://example.com/".to_string()), support: Some(serde_json::json!({"source": "https://github.com/v/p"})), - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }; let e = installed_to_entry(&pkg); assert_eq!(e.support_source.as_deref(), Some("https://github.com/v/p")); @@ -630,7 +628,7 @@ mod tests { aliases: vec![], homepage: None, support: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }); installed.upsert(mozart_core::repository::installed::InstalledPackageEntry { name: "b/b".to_string(), @@ -644,11 +642,11 @@ mod tests { aliases: vec![], homepage: None, support: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }); installed.write(&vendor_dir).unwrap(); - let mut root_req = BTreeMap::new(); + let mut root_req = indexmap::IndexMap::new(); root_req.insert("a/a".to_string(), "*".to_string()); let kept = load_installed_entries(working_dir, &root_req, true).unwrap(); @@ -679,11 +677,11 @@ mod tests { version_normalized: None, source: None, dist: 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(), suggest: None, package_type: None, autoload: None, @@ -696,7 +694,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }], packages_dev: Some(vec![LockedPackage { name: "phpunit/phpunit".to_string(), @@ -704,11 +702,11 @@ mod tests { version_normalized: None, source: None, dist: 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(), suggest: None, package_type: None, autoload: None, @@ -721,7 +719,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }]), aliases: vec![], minimum_stability: "stable".to_string(), diff --git a/crates/mozart/src/commands/remove.rs b/crates/mozart/src/commands/remove.rs index 5bb3be3..938a5d1 100644 --- a/crates/mozart/src/commands/remove.rs +++ b/crates/mozart/src/commands/remove.rs @@ -154,7 +154,7 @@ pub async fn execute( if args.dev { if composer.require_dev.contains_key(&name) { console_writeln!(io, "<info>Removing {name} from require-dev</info>"); - composer.require_dev.remove(&name); + composer.require_dev.shift_remove(&name); packages_removed.push(name); } else { io.lock().unwrap().info(&console_format!( @@ -163,11 +163,11 @@ pub async fn execute( } } else if composer.require.contains_key(&name) { console_writeln!(io, "<info>Removing {name} from require</info>"); - composer.require.remove(&name); + composer.require.shift_remove(&name); packages_removed.push(name); } else if composer.require_dev.contains_key(&name) { console_writeln!(io, "<info>Removing {name} from require-dev</info>"); - composer.require_dev.remove(&name); + composer.require_dev.shift_remove(&name); packages_removed.push(name); } else { io.lock().unwrap().info(&console_format!( @@ -682,7 +682,6 @@ mod tests { use super::*; use mozart_core::package::RawPackageData; use mozart_core::repository::lockfile; - use std::collections::BTreeMap; fn make_locked_package(name: &str, version: &str) -> lockfile::LockedPackage { lockfile::LockedPackage { @@ -691,11 +690,11 @@ mod tests { version_normalized: Some(format!("{}.0", version)), source: None, dist: 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(), suggest: None, package_type: Some("library".to_string()), autoload: None, @@ -708,7 +707,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } @@ -746,7 +745,7 @@ mod tests { assert!(composer.require.contains_key("psr/log")); - composer.require.remove("psr/log"); + composer.require.shift_remove("psr/log"); assert!( !composer.require.contains_key("psr/log"), @@ -771,7 +770,7 @@ mod tests { assert!(composer.require_dev.contains_key("phpunit/phpunit")); - composer.require_dev.remove("phpunit/phpunit"); + composer.require_dev.shift_remove("phpunit/phpunit"); assert!( !composer.require_dev.contains_key("phpunit/phpunit"), @@ -792,8 +791,8 @@ mod tests { .insert("psr/log".to_string(), "^3.0".to_string()); let name = "nonexistent/package"; - let found_in_require = composer.require.remove(name).is_some(); - let found_in_require_dev = composer.require_dev.remove(name).is_some(); + let found_in_require = composer.require.shift_remove(name).is_some(); + let found_in_require_dev = composer.require_dev.shift_remove(name).is_some(); assert!(!found_in_require); assert!(!found_in_require_dev); @@ -814,9 +813,9 @@ mod tests { .insert("phpunit/phpunit".to_string(), "^11.0".to_string()); let name = "psr/log"; - let removed_from_require = composer.require.remove(name).is_some(); + let removed_from_require = composer.require.shift_remove(name).is_some(); let removed_from_dev = if !removed_from_require { - composer.require_dev.remove(name).is_some() + composer.require_dev.shift_remove(name).is_some() } else { false }; @@ -842,9 +841,9 @@ mod tests { .insert("phpunit/phpunit".to_string(), "^11.0".to_string()); let name = "phpunit/phpunit"; - let removed_from_require = composer.require.remove(name).is_some(); + let removed_from_require = composer.require.shift_remove(name).is_some(); let removed_from_dev = if !removed_from_require { - composer.require_dev.remove(name).is_some() + composer.require_dev.shift_remove(name).is_some() } else { false }; @@ -894,8 +893,8 @@ mod tests { // A glob-style name: not a valid exact package name, not in require either. let name = "vendor/*"; - let found = - composer.require.remove(name).is_some() || composer.require_dev.remove(name).is_some(); + let found = composer.require.shift_remove(name).is_some() + || composer.require_dev.shift_remove(name).is_some(); // Should NOT be found (falls through to "not required" warning), not panicked/bailed. assert!(!found, "glob name should not match any package"); @@ -1001,7 +1000,7 @@ mod tests { .write_to_file(&lock_path) .expect("should write initial lock file"); - composer.require.remove("psr/log"); + composer.require.shift_remove("psr/log"); package::write_to_file(&composer, &composer_path).unwrap(); let request2 = ResolveRequest { @@ -1084,7 +1083,7 @@ mod tests { std::fs::write(&composer_path, content).unwrap(); let mut composer: RawPackageData = serde_json::from_str(content).unwrap(); - composer.require.remove("psr/log"); + composer.require.shift_remove("psr/log"); package::write_to_file(&composer, &composer_path).unwrap(); assert!( diff --git a/crates/mozart/src/commands/require.rs b/crates/mozart/src/commands/require.rs index 3d5fcd6..f92442c 100644 --- a/crates/mozart/src/commands/require.rs +++ b/crates/mozart/src/commands/require.rs @@ -1026,9 +1026,9 @@ pub async fn execute( // Remove from the opposite section before inserting into the target. for pkg in &inconsistent { if args.dev { - raw.require.remove(pkg.as_str()); + raw.require.shift_remove(pkg.as_str()); } else { - raw.require_dev.remove(pkg.as_str()); + raw.require_dev.shift_remove(pkg.as_str()); } } @@ -1066,10 +1066,8 @@ pub async fn execute( let sort_packages = args.sort_packages || config_sort_packages; if sort_packages { - let sorted_require: std::collections::BTreeMap<_, _> = raw.require.clone(); - raw.require = sorted_require; - let sorted_dev: std::collections::BTreeMap<_, _> = raw.require_dev.clone(); - raw.require_dev = sorted_dev; + raw.require.sort_unstable_keys(); + raw.require_dev.sort_unstable_keys(); } // --- Write composer.json (unless --dry-run) --- @@ -1129,7 +1127,6 @@ pub async fn execute( #[cfg(test)] mod tests { use super::*; - use std::collections::BTreeMap; fn make_locked_package(name: &str, version: &str) -> lockfile::LockedPackage { lockfile::LockedPackage { @@ -1138,11 +1135,11 @@ mod tests { version_normalized: Some(format!("{}.0", version)), source: None, dist: 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(), suggest: None, package_type: Some("library".to_string()), autoload: None, @@ -1155,7 +1152,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } @@ -1176,35 +1173,6 @@ mod tests { } } - /// Verify that --sort-packages sorts both require and require-dev maps. - #[test] - fn test_sort_packages_sorts_both_sections() { - use mozart_core::package::RawPackageData; - - let mut raw = RawPackageData::new("test/project".to_string()); - raw.require - .insert("z/package".to_string(), "^1.0".to_string()); - raw.require - .insert("a/package".to_string(), "^2.0".to_string()); - raw.require - .insert("m/package".to_string(), "^3.0".to_string()); - raw.require_dev - .insert("z/dev".to_string(), "^1.0".to_string()); - raw.require_dev - .insert("a/dev".to_string(), "^2.0".to_string()); - - let sorted_require: BTreeMap<String, String> = raw.require.clone(); - raw.require = sorted_require; - let sorted_dev: BTreeMap<String, String> = raw.require_dev.clone(); - raw.require_dev = sorted_dev; - - let require_keys: Vec<_> = raw.require.keys().collect(); - assert_eq!(require_keys, vec!["a/package", "m/package", "z/package"]); - - let dev_keys: Vec<_> = raw.require_dev.keys().collect(); - assert_eq!(dev_keys, vec!["a/dev", "z/dev"]); - } - /// Verify that compute_update_changes produces correct Install entries for new packages. #[test] fn test_require_change_report_new_packages() { diff --git a/crates/mozart/src/commands/show.rs b/crates/mozart/src/commands/show.rs index d0a2218..81eaaad 100644 --- a/crates/mozart/src/commands/show.rs +++ b/crates/mozart/src/commands/show.rs @@ -6,7 +6,6 @@ use mozart_core::console_writeln; use mozart_core::console_writeln_error; use mozart_core::matches_wildcard; use mozart_core::platform::is_platform_package; -use std::collections::BTreeMap; use std::path::Path; #[derive(Default, Args)] @@ -255,15 +254,15 @@ struct PackageDetail { support: Option<serde_json::Value>, /// A13: autoload rules. autoload: Option<serde_json::Value>, - require: BTreeMap<String, String>, - require_dev: BTreeMap<String, String>, + require: indexmap::IndexMap<String, String>, + require_dev: indexmap::IndexMap<String, String>, /// A12: conflict links. - conflict: BTreeMap<String, String>, + conflict: indexmap::IndexMap<String, String>, /// A12: provide links. - provide: BTreeMap<String, String>, + provide: indexmap::IndexMap<String, String>, /// A12: replace links. - replace: BTreeMap<String, String>, - suggest: BTreeMap<String, String>, + replace: indexmap::IndexMap<String, String>, + suggest: indexmap::IndexMap<String, String>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -1132,7 +1131,7 @@ async fn print_package_detail( /// Print a named section of package links (requires, conflict, etc.). fn print_links_section( label: &str, - links: &BTreeMap<String, String>, + links: &indexmap::IndexMap<String, String>, io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, ) { if links.is_empty() { @@ -2077,7 +2076,7 @@ fn get_installed_release_date( fn get_installed_link_map( pkg: &mozart_core::repository::installed::InstalledPackageEntry, key: &str, -) -> BTreeMap<String, String> { +) -> indexmap::IndexMap<String, String> { pkg.extra_fields .get(key) .and_then(|v| v.as_object()) @@ -2092,7 +2091,7 @@ fn get_installed_link_map( /// Extract a map of `{package: reason}` from an installed package's suggest field. fn get_installed_suggest_map( pkg: &mozart_core::repository::installed::InstalledPackageEntry, -) -> BTreeMap<String, String> { +) -> indexmap::IndexMap<String, String> { pkg.extra_fields .get("suggest") .and_then(|v| v.as_object()) @@ -2268,8 +2267,7 @@ mod tests { #[test] fn test_get_installed_description_present() { - use std::collections::BTreeMap; - let mut extra = BTreeMap::new(); + let mut extra = indexmap::IndexMap::new(); extra.insert( "description".to_string(), serde_json::Value::String("A logging library".to_string()), @@ -2293,7 +2291,6 @@ mod tests { #[test] fn test_get_installed_description_absent() { - use std::collections::BTreeMap; let pkg = mozart_core::repository::installed::InstalledPackageEntry { name: "psr/log".to_string(), version: "3.0.0".to_string(), @@ -2306,15 +2303,14 @@ mod tests { aliases: vec![], homepage: None, support: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }; assert_eq!(get_installed_description(&pkg), ""); } #[test] fn test_get_installed_keywords() { - use std::collections::BTreeMap; - let mut extra = BTreeMap::new(); + let mut extra = indexmap::IndexMap::new(); extra.insert( "keywords".to_string(), serde_json::json!(["log", "psr3", "logging"]), diff --git a/crates/mozart/src/commands/suggests.rs b/crates/mozart/src/commands/suggests.rs index b882f5c..ca8e5fe 100644 --- a/crates/mozart/src/commands/suggests.rs +++ b/crates/mozart/src/commands/suggests.rs @@ -235,11 +235,10 @@ fn build_root_info(root: Option<&mozart_core::package::RawPackageData>) -> RootI mod tests { use super::*; use mozart_core::installer::{HasSuggests, InstalledRepoLite, RootInfo}; - use std::collections::BTreeMap; fn make_locked_package( name: &str, - suggest: Option<BTreeMap<String, String>>, + suggest: Option<indexmap::IndexMap<String, String>>, ) -> mozart_core::repository::lockfile::LockedPackage { mozart_core::repository::lockfile::LockedPackage { name: name.to_string(), @@ -247,11 +246,11 @@ mod tests { version_normalized: None, source: None, dist: 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(), suggest, package_type: None, autoload: None, @@ -264,15 +263,15 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } fn make_installed_entry( name: &str, - suggest: Option<BTreeMap<String, String>>, + suggest: Option<indexmap::IndexMap<String, String>>, ) -> mozart_core::repository::installed::InstalledPackageEntry { - let mut extra_fields: BTreeMap<String, serde_json::Value> = BTreeMap::new(); + let mut extra_fields = indexmap::IndexMap::new(); if let Some(s) = suggest { let map: serde_json::Map<String, serde_json::Value> = s .into_iter() @@ -322,7 +321,7 @@ mod tests { #[test] fn locked_package_implements_has_suggests() { - let mut suggest = BTreeMap::new(); + let mut suggest = indexmap::IndexMap::new(); suggest.insert("ext-intl".to_string(), "for i18n".to_string()); suggest.insert("ext-redis".to_string(), "for cache".to_string()); let pkg = make_locked_package("vendor/a", Some(suggest)); @@ -333,7 +332,7 @@ mod tests { #[test] fn installed_entry_reads_suggest_from_extra_fields() { - let mut suggest = BTreeMap::new(); + let mut suggest = indexmap::IndexMap::new(); suggest.insert("ext-redis".to_string(), "for cache".to_string()); let entry = make_installed_entry("vendor/cache", Some(suggest)); let pairs = entry.suggests(); @@ -413,15 +412,15 @@ mod tests { license: None, authors: vec![], 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![], autoload: None, bin: vec![], - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), }; root.require.insert("vendor/a".into(), "^1.0".into()); root.require_dev.insert("vendor/b".into(), "^2.0".into()); @@ -437,7 +436,7 @@ mod tests { let console = console(); let mut reporter = SuggestedPackagesReporter::new(&console); - let mut suggest = BTreeMap::new(); + let mut suggest = indexmap::IndexMap::new(); suggest.insert("ext-intl".to_string(), "for i18n".to_string()); suggest.insert("vendor/optional".to_string(), "Optional".to_string()); let pkg = make_locked_package("vendor/a", Some(suggest)); @@ -452,7 +451,7 @@ mod tests { let console = console(); let mut reporter = SuggestedPackagesReporter::new(&console); - let mut suggest = BTreeMap::new(); + let mut suggest = indexmap::IndexMap::new(); suggest.insert("vendor/already-here".to_string(), "".to_string()); suggest.insert("vendor/not-here".to_string(), "".to_string()); let pkg = make_locked_package("vendor/a", Some(suggest)); diff --git a/crates/mozart/src/commands/update.rs b/crates/mozart/src/commands/update.rs index f482272..334e221 100644 --- a/crates/mozart/src/commands/update.rs +++ b/crates/mozart/src/commands/update.rs @@ -1803,7 +1803,6 @@ pub async fn run( #[cfg(test)] mod tests { use super::*; - use std::collections::BTreeMap; fn make_locked_package(name: &str, version: &str) -> lockfile::LockedPackage { lockfile::LockedPackage { @@ -1812,11 +1811,11 @@ mod tests { version_normalized: Some(format!("{}.0", version)), source: None, dist: 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(), suggest: None, package_type: Some("library".to_string()), autoload: None, @@ -1829,7 +1828,7 @@ mod tests { support: None, funding: None, time: None, - extra_fields: BTreeMap::new(), + extra_fields: indexmap::IndexMap::new(), } } diff --git a/crates/mozart/src/factory.rs b/crates/mozart/src/factory.rs index 41f157a..581b53a 100644 --- a/crates/mozart/src/factory.rs +++ b/crates/mozart/src/factory.rs @@ -368,7 +368,7 @@ mod tests { r#"{"name": "acme/app", "require": {"vendor/pkg": "^1.0"}}"#, ); - use mozart_core::package::Package; + use mozart_core::package::PackageInterface; let composer = Composer::require(io(), dir.path()).unwrap(); assert_eq!(composer.package().name(), "acme/app"); assert_eq!( |
