aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/mozart-autoload/Cargo.toml1
-rw-r--r--crates/mozart-autoload/src/autoload.rs7
-rw-r--r--crates/mozart-core/Cargo.toml2
-rw-r--r--crates/mozart-registry/Cargo.toml1
-rw-r--r--crates/mozart-registry/src/downloader.rs4
-rw-r--r--crates/mozart-registry/src/inline_package.rs6
-rw-r--r--crates/mozart-registry/src/lockfile.rs26
-rw-r--r--crates/mozart-registry/src/repository/mod.rs4
-rw-r--r--crates/mozart-registry/src/resolver.rs58
-rw-r--r--crates/mozart-registry/src/vcs_bridge.rs7
-rw-r--r--crates/mozart-sat-resolver/Cargo.toml1
-rw-r--r--crates/mozart-sat-resolver/src/decisions.rs6
-rw-r--r--crates/mozart-sat-resolver/src/policy.rs4
-rw-r--r--crates/mozart-sat-resolver/src/pool.rs10
-rw-r--r--crates/mozart-sat-resolver/src/pool_builder.rs17
-rw-r--r--crates/mozart-sat-resolver/src/problem.rs4
-rw-r--r--crates/mozart-sat-resolver/src/request.rs6
-rw-r--r--crates/mozart-sat-resolver/src/rule_set.rs6
-rw-r--r--crates/mozart-sat-resolver/src/rule_set_generator.rs36
-rw-r--r--crates/mozart-sat-resolver/src/rule_watch_graph.rs6
-rw-r--r--crates/mozart-sat-resolver/src/solver.rs36
-rw-r--r--crates/mozart-sat-resolver/src/transaction.rs24
-rw-r--r--crates/mozart-spdx-licenses/Cargo.toml1
-rw-r--r--crates/mozart-spdx-licenses/src/lib.rs14
-rw-r--r--crates/mozart-test-harness/Cargo.toml1
-rw-r--r--crates/mozart-test-harness/src/parser.rs8
-rw-r--r--crates/mozart-vcs/Cargo.toml1
-rw-r--r--crates/mozart-vcs/src/driver/bitbucket.rs7
-rw-r--r--crates/mozart-vcs/src/driver/forgejo.rs7
-rw-r--r--crates/mozart-vcs/src/driver/git.rs9
-rw-r--r--crates/mozart-vcs/src/driver/github.rs7
-rw-r--r--crates/mozart-vcs/src/driver/gitlab.rs7
-rw-r--r--crates/mozart-vcs/src/driver/hg.rs7
-rw-r--r--crates/mozart-vcs/src/driver/svn.rs7
-rw-r--r--crates/mozart-vcs/src/process.rs8
-rw-r--r--crates/mozart/Cargo.toml1
-rw-r--r--crates/mozart/src/commands/audit.rs4
-rw-r--r--crates/mozart/src/commands/bump.rs6
-rw-r--r--crates/mozart/src/commands/check_platform_reqs.rs2
-rw-r--r--crates/mozart/src/commands/create_project.rs6
-rw-r--r--crates/mozart/src/commands/dependency.rs13
-rw-r--r--crates/mozart/src/commands/exec.rs3
-rw-r--r--crates/mozart/src/commands/install.rs15
-rw-r--r--crates/mozart/src/commands/licenses.rs4
-rw-r--r--crates/mozart/src/commands/outdated.rs10
-rw-r--r--crates/mozart/src/commands/reinstall.rs4
-rw-r--r--crates/mozart/src/commands/remove.rs30
-rw-r--r--crates/mozart/src/commands/repository.rs2
-rw-r--r--crates/mozart/src/commands/require.rs27
-rw-r--r--crates/mozart/src/commands/search.rs4
-rw-r--r--crates/mozart/src/commands/show.rs20
-rw-r--r--crates/mozart/src/commands/status.rs30
-rw-r--r--crates/mozart/src/commands/suggests.rs44
-rw-r--r--crates/mozart/src/commands/update.rs36
-rw-r--r--crates/mozart/src/commands/validate.rs2
55 files changed, 321 insertions, 298 deletions
diff --git a/crates/mozart-autoload/Cargo.toml b/crates/mozart-autoload/Cargo.toml
index 98c4f6f..1f1ed5a 100644
--- a/crates/mozart-autoload/Cargo.toml
+++ b/crates/mozart-autoload/Cargo.toml
@@ -7,6 +7,7 @@ edition.workspace = true
mozart-class-map-generator.workspace = true
mozart-registry.workspace = true
anyhow.workspace = true
+indexmap.workspace = true
md5.workspace = true
serde_json.workspace = true
diff --git a/crates/mozart-autoload/src/autoload.rs b/crates/mozart-autoload/src/autoload.rs
index 069fcfa..5e5d702 100644
--- a/crates/mozart-autoload/src/autoload.rs
+++ b/crates/mozart-autoload/src/autoload.rs
@@ -1,7 +1,8 @@
+use indexmap::IndexSet;
use mozart_class_map_generator::{scan_classmap_dirs, scan_psr_for_classmap};
use mozart_registry::installed::InstalledPackages;
use mozart_registry::lockfile::LockedPackage;
-use std::collections::{BTreeMap, HashSet};
+use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
// Embed Composer PHP files from the submodule at compile time.
@@ -470,7 +471,7 @@ fn generate_platform_check(
packages: &[LockedPackage],
root_require: Option<&serde_json::Value>,
mode: &PlatformCheckMode,
- dev_package_names: &HashSet<String>,
+ dev_package_names: &IndexSet<String>,
) -> Option<String> {
if matches!(mode, PlatformCheckMode::Disabled) {
return None;
@@ -934,7 +935,7 @@ pub fn generate(config: &AutoloadConfig) -> anyhow::Result<GenerateResult> {
}
// 4a. Generate platform_check.php if needed
- let dev_package_names_set: HashSet<String> = installed
+ let dev_package_names_set: IndexSet<String> = installed
.dev_package_names
.iter()
.map(|n| n.to_lowercase())
diff --git a/crates/mozart-core/Cargo.toml b/crates/mozart-core/Cargo.toml
index 391c310..fb114ff 100644
--- a/crates/mozart-core/Cargo.toml
+++ b/crates/mozart-core/Cargo.toml
@@ -4,6 +4,7 @@ version.workspace = true
edition.workspace = true
[dependencies]
+mozart-console-macros.workspace = true
mozart-spdx-licenses.workspace = true
anyhow.workspace = true
colored.workspace = true
@@ -11,7 +12,6 @@ dialoguer.workspace = true
regex.workspace = true
serde.workspace = true
serde_json.workspace = true
-mozart-console-macros = { version = "0.1.0", path = "../mozart-console-macros" }
[dev-dependencies]
tempfile.workspace = true
diff --git a/crates/mozart-registry/Cargo.toml b/crates/mozart-registry/Cargo.toml
index abde30d..83e42b5 100644
--- a/crates/mozart-registry/Cargo.toml
+++ b/crates/mozart-registry/Cargo.toml
@@ -13,6 +13,7 @@ anyhow.workspace = true
async-trait.workspace = true
filetime.workspace = true
flate2.workspace = true
+indexmap.workspace = true
md5.workspace = true
reqwest.workspace = true
serde.workspace = true
diff --git a/crates/mozart-registry/src/downloader.rs b/crates/mozart-registry/src/downloader.rs
index 8c1f0b0..c13ebdc 100644
--- a/crates/mozart-registry/src/downloader.rs
+++ b/crates/mozart-registry/src/downloader.rs
@@ -1,6 +1,6 @@
use crate::cache::Cache;
+use indexmap::IndexSet;
use sha1::{Digest, Sha1};
-use std::collections::HashSet;
use std::fs;
use std::io::{Cursor, Read, Write};
use std::path::Path;
@@ -168,7 +168,7 @@ fn find_top_level_dir(entries: &[String]) -> Option<String> {
return None;
}
- let mut prefixes: HashSet<String> = HashSet::new();
+ let mut prefixes: IndexSet<String> = IndexSet::new();
for entry in entries {
let slash_pos = entry.find('/')?;
prefixes.insert(entry[..slash_pos + 1].to_string());
diff --git a/crates/mozart-registry/src/inline_package.rs b/crates/mozart-registry/src/inline_package.rs
index bad00fb..0cc38d6 100644
--- a/crates/mozart-registry/src/inline_package.rs
+++ b/crates/mozart-registry/src/inline_package.rs
@@ -6,8 +6,8 @@
//! pool and into the generated lockfile entry verbatim.
use crate::packagist::PackagistVersion;
+use indexmap::IndexSet;
use mozart_core::package::RawRepository;
-use std::collections::HashSet;
/// One package extracted from a `type: package` repository.
pub struct InlinePackage {
@@ -28,7 +28,7 @@ pub struct InlinePackage {
/// first-repo-wins priority via `RepositorySet::findPackages`.
pub fn collect_inline_packages(repositories: &[RawRepository]) -> Vec<InlinePackage> {
let mut packages = Vec::new();
- let mut claimed: HashSet<String> = HashSet::new();
+ let mut claimed: IndexSet<String> = IndexSet::new();
for repo in repositories {
if repo.repo_type != "package" {
continue;
@@ -54,7 +54,7 @@ pub fn collect_inline_packages(repositories: &[RawRepository]) -> Vec<InlinePack
_ => {}
}
- let mut names_this_repo: HashSet<String> = HashSet::new();
+ let mut names_this_repo: IndexSet<String> = IndexSet::new();
for pkg in from_this_repo {
if claimed.contains(&pkg.name) {
continue;
diff --git a/crates/mozart-registry/src/lockfile.rs b/crates/mozart-registry/src/lockfile.rs
index 99e87c8..de2c030 100644
--- a/crates/mozart-registry/src/lockfile.rs
+++ b/crates/mozart-registry/src/lockfile.rs
@@ -1,9 +1,11 @@
use crate::packagist::{PackagistDist, PackagistSource, PackagistVersion};
use crate::repository::RepositorySet;
use crate::resolver::ResolvedPackage;
+use indexmap::IndexMap;
+use indexmap::IndexSet;
use mozart_core::package::{RawPackageData, to_json_pretty};
use serde::{Deserialize, Serialize};
-use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
+use std::collections::{BTreeMap, VecDeque};
use std::fs;
use std::path::Path;
@@ -566,14 +568,14 @@ fn classify_dev_packages(
resolved: &[ResolvedPackage],
require: &BTreeMap<String, String>,
_require_dev: &BTreeMap<String, String>,
- package_metadata: &HashMap<String, PackagistVersion>,
-) -> HashSet<String> {
+ package_metadata: &IndexMap<String, PackagistVersion>,
+) -> IndexSet<String> {
// Build set of all resolved package names for quick lookup
- let resolved_names: HashSet<&str> = resolved.iter().map(|p| p.name.as_str()).collect();
+ let resolved_names: IndexSet<&str> = resolved.iter().map(|p| p.name.as_str()).collect();
// BFS from non-dev root dependencies through each package's `require` map.
// All reachable packages are production packages.
- let mut production: HashSet<String> = HashSet::new();
+ let mut production: IndexSet<String> = IndexSet::new();
let mut queue: VecDeque<String> = VecDeque::new();
// Seed queue with non-dev root dependencies that are actual packages (not platform)
@@ -659,7 +661,7 @@ pub async fn generate_lock_file(request: &LockFileGenerationRequest) -> anyhow::
// — short-circuit those before hitting the network. Everything else goes
// through `RepositorySet`, which today contains only Packagist; future
// steps will move VCS / inline through the same set.
- let mut package_metadata: HashMap<String, PackagistVersion> = HashMap::new();
+ let mut package_metadata: IndexMap<String, PackagistVersion> = IndexMap::new();
let repo_set = &request.repositories;
for pkg in &real_resolved {
if let Some(inline) = request.inline_lookup(&pkg.name, &pkg.version_normalized) {
@@ -1120,7 +1122,7 @@ mod tests {
let mut require_dev = BTreeMap::new();
require_dev.insert("vendor/b".to_string(), "^1.0".to_string());
- let mut metadata: HashMap<String, PackagistVersion> = HashMap::new();
+ let mut metadata: IndexMap<String, PackagistVersion> = IndexMap::new();
// A requires C
let mut a_require = BTreeMap::new();
@@ -1196,7 +1198,7 @@ mod tests {
let mut require_dev = BTreeMap::new();
require_dev.insert("vendor/b".to_string(), "^1.0".to_string());
- let mut metadata: HashMap<String, PackagistVersion> = HashMap::new();
+ let mut metadata: IndexMap<String, PackagistVersion> = IndexMap::new();
// A requires C
let mut a_require = BTreeMap::new();
@@ -1388,7 +1390,7 @@ mod tests {
require_dev: vec![],
include_dev: false,
minimum_stability: Stability::Stable,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable: true,
prefer_lowest: false,
platform: PlatformConfig::new(),
@@ -1398,10 +1400,10 @@ mod tests {
std::env::temp_dir().join("mozart-test-cache"),
false,
))),
- temporary_constraints: HashMap::new(),
+ temporary_constraints: IndexMap::new(),
raw_repositories: vec![],
- root_provide: HashMap::new(),
- root_replace: HashMap::new(),
+ root_provide: IndexMap::new(),
+ root_replace: IndexMap::new(),
};
let resolved = resolve(&resolve_request)
diff --git a/crates/mozart-registry/src/repository/mod.rs b/crates/mozart-registry/src/repository/mod.rs
index 0f742a3..21752b9 100644
--- a/crates/mozart-registry/src/repository/mod.rs
+++ b/crates/mozart-registry/src/repository/mod.rs
@@ -117,10 +117,10 @@ impl RepositorySet {
&self,
queries: &[PackageQuery<'_>],
) -> anyhow::Result<Vec<NamedPackagistVersion>> {
- use std::collections::HashSet;
+ use indexmap::IndexSet;
let mut packages: Vec<NamedPackagistVersion> = Vec::new();
- let mut answered: HashSet<String> = HashSet::new();
+ let mut answered: IndexSet<String> = IndexSet::new();
for repo in &self.repos {
let pending: Vec<PackageQuery<'_>> = queries
diff --git a/crates/mozart-registry/src/resolver.rs b/crates/mozart-registry/src/resolver.rs
index 6499130..89d3a68 100644
--- a/crates/mozart-registry/src/resolver.rs
+++ b/crates/mozart-registry/src/resolver.rs
@@ -4,7 +4,7 @@
//! candidate packages, generates SAT rules, and runs the CDCL solver to find
//! a compatible set of packages to install.
-use std::collections::{HashMap, HashSet};
+use indexmap::{IndexMap, IndexSet};
use std::fmt;
use std::sync::Arc;
@@ -275,7 +275,7 @@ impl PackageName {
/// Platform package configuration.
/// Maps package names to version strings (normalized, e.g. "8.1.0.0").
pub struct PlatformConfig {
- pub packages: HashMap<String, String>,
+ pub packages: IndexMap<String, String>,
}
impl Default for PlatformConfig {
@@ -288,7 +288,7 @@ impl PlatformConfig {
/// Detect platform packages from the local PHP installation.
pub fn new() -> Self {
let detected = mozart_core::platform::detect_platform();
- let mut packages = HashMap::new();
+ let mut packages = IndexMap::new();
for pkg in detected {
packages.insert(pkg.name, pkg.version);
}
@@ -308,7 +308,7 @@ impl PlatformConfig {
for (name, value) in obj {
let key = name.to_lowercase();
if value.as_bool() == Some(false) {
- self.packages.remove(&key);
+ self.packages.shift_remove(&key);
continue;
}
if let Some(s) = value.as_str() {
@@ -318,7 +318,7 @@ impl PlatformConfig {
}
/// Parse platform packages into `Version` values.
- pub fn to_versions(&self) -> HashMap<String, Version> {
+ pub fn to_versions(&self) -> IndexMap<String, Version> {
self.packages
.iter()
.filter_map(|(name, version_str)| {
@@ -380,7 +380,7 @@ fn passes_stability_filter(
package_name: &str,
version: &Version,
minimum_stability: Stability,
- stability_flags: &HashMap<String, Stability>,
+ stability_flags: &IndexMap<String, Stability>,
) -> bool {
let min_stability = stability_flags
.get(package_name)
@@ -417,7 +417,7 @@ fn packagist_to_pool_inputs(
package_name: &str,
pv: &packagist::PackagistVersion,
minimum_stability: Stability,
- stability_flags: &HashMap<String, Stability>,
+ stability_flags: &IndexMap<String, Stability>,
) -> Vec<PoolPackageInput> {
let mut results = Vec::new();
@@ -570,7 +570,7 @@ pub struct ResolveRequest {
/// Minimum stability from composer.json.
pub minimum_stability: Stability,
/// Per-package stability overrides.
- pub stability_flags: HashMap<String, Stability>,
+ pub stability_flags: IndexMap<String, Stability>,
/// Whether prefer-stable is enabled.
pub prefer_stable: bool,
/// Whether prefer-lowest is enabled.
@@ -589,7 +589,7 @@ pub struct ResolveRequest {
pub repositories: Arc<RepositorySet>,
/// Temporary version constraint overrides (from --with flag).
/// Maps package name (lowercase) to constraint string.
- pub temporary_constraints: HashMap<String, String>,
+ pub temporary_constraints: IndexMap<String, String>,
/// VCS / inline-package repository entries from composer.json's
/// `repositories` section, used by the eager VCS scan and inline-package
/// preload that still live in `resolve()` (Step B follow-up will move
@@ -600,10 +600,10 @@ pub struct ResolveRequest {
/// `require` names something the root itself `provide`s with a matching
/// constraint, no install-one-of rule is emitted, mirroring Composer's
/// `RuleSetGenerator::createRequireRule` self-fulfillment branch.
- pub root_provide: HashMap<String, String>,
+ pub root_provide: IndexMap<String, String>,
/// Root composer.json's `replace` map. Same role as `root_provide` for the
/// `replace` link: a replaced target counts as fulfilled by the root.
- pub root_replace: HashMap<String, String>,
+ pub root_replace: IndexMap<String, String>,
}
/// A single package in the resolution output.
@@ -633,12 +633,12 @@ pub struct ResolvedPackage {
/// or a human-readable error.
pub async fn resolve(request: &ResolveRequest) -> Result<Vec<ResolvedPackage>, ResolveError> {
// 1. Build root requirements
- let mut root_requires: HashMap<String, Option<String>> = HashMap::new();
+ let mut root_requires: IndexMap<String, Option<String>> = IndexMap::new();
// Per-package stability overrides extracted from `@dev`/`@beta`/etc.
// suffixes on root constraints. Mirrors Composer's
// `RootPackageLoader::extractStabilityFlags`. Merged on top of the
// request's caller-supplied flags (which today are usually empty).
- let mut stability_flags: HashMap<String, Stability> = request.stability_flags.clone();
+ let mut stability_flags: IndexMap<String, Stability> = request.stability_flags.clone();
let minimum_stability = request.minimum_stability;
let mut insert_root_require = |name: &str, constraint: &str| {
@@ -700,7 +700,7 @@ pub async fn resolve(request: &ResolveRequest) -> Result<Vec<ResolvedPackage>, R
let mut builder = PoolBuilder::new();
// Set up ignore list for platform requirements
- let mut ignore_set: HashSet<String> = HashSet::new();
+ let mut ignore_set: IndexSet<String> = IndexSet::new();
for name in &request.ignore_platform_req_list {
ignore_set.insert(name.clone());
}
@@ -709,7 +709,7 @@ pub async fn resolve(request: &ResolveRequest) -> Result<Vec<ResolvedPackage>, R
// Add platform packages as fixed entries
let platform_config = request.platform.to_versions();
- let mut fixed_packages_by_name: HashMap<String, u32> = HashMap::new();
+ let mut fixed_packages_by_name: IndexMap<String, u32> = IndexMap::new();
for (name, version) in &platform_config {
if should_skip_platform_dep(
name,
@@ -734,7 +734,7 @@ pub async fn resolve(request: &ResolveRequest) -> Result<Vec<ResolvedPackage>, R
// Scan VCS repositories and collect packages from them
let vcs_packages = vcs_bridge::scan_vcs_repositories(&request.raw_repositories).await;
- let mut vcs_package_names: HashSet<String> = HashSet::new();
+ let mut vcs_package_names: IndexSet<String> = IndexSet::new();
for vpkg in &vcs_packages {
vcs_package_names.insert(vpkg.name.clone());
}
@@ -752,7 +752,7 @@ pub async fn resolve(request: &ResolveRequest) -> Result<Vec<ResolvedPackage>, R
// network fetch; they go straight into the pool and are also tracked by
// name so the Packagist seed/transitive loops below skip them.
let inline_packages = crate::inline_package::collect_inline_packages(&request.raw_repositories);
- let mut inline_package_names: HashSet<String> = HashSet::new();
+ let mut inline_package_names: IndexSet<String> = IndexSet::new();
for ipkg in &inline_packages {
inline_package_names.insert(ipkg.name.clone());
let inputs = packagist_to_pool_inputs(
@@ -773,7 +773,7 @@ pub async fn resolve(request: &ResolveRequest) -> Result<Vec<ResolvedPackage>, R
// into the pool, with names recorded so Packagist loops skip them.
let composer_repo_packages =
crate::composer_repo::collect_composer_packages(&request.raw_repositories);
- let mut composer_repo_names: HashSet<String> = HashSet::new();
+ let mut composer_repo_names: IndexSet<String> = IndexSet::new();
for cpkg in &composer_repo_packages {
composer_repo_names.insert(cpkg.name.clone());
let inputs = packagist_to_pool_inputs(
@@ -917,7 +917,7 @@ pub async fn resolve(request: &ResolveRequest) -> Result<Vec<ResolvedPackage>, R
// Create policy and solve
let policy = DefaultPolicy::new(request.prefer_stable, request.prefer_lowest);
- let fixed_set: HashSet<u32> = fixed_ids.into_iter().collect();
+ let fixed_set: IndexSet<u32> = fixed_ids.into_iter().collect();
let solver = Solver::new(rules, &pool, policy, fixed_set);
match solver.solve() {
@@ -1143,7 +1143,7 @@ mod tests {
let rc_v = v_pre(1, 0, 0, 0, "RC1");
let dev_v = v_pre(1, 0, 0, 0, "dev");
- let flags = HashMap::new();
+ let flags = IndexMap::new();
assert!(passes_stability_filter(
"foo/foo",
@@ -1184,7 +1184,7 @@ mod tests {
let alpha_v = v_pre(1, 0, 0, 0, "alpha1");
let dev_v = v_pre(1, 0, 0, 0, "dev");
- let flags = HashMap::new();
+ let flags = IndexMap::new();
assert!(passes_stability_filter(
"foo/foo",
@@ -1215,7 +1215,7 @@ mod tests {
#[test]
fn test_stability_filter_dev() {
let dev_v = v_pre(1, 0, 0, 0, "dev");
- let flags = HashMap::new();
+ let flags = IndexMap::new();
assert!(passes_stability_filter(
"foo/foo",
&dev_v,
@@ -1308,14 +1308,14 @@ mod tests {
vec![],
);
- let mut requires = HashMap::new();
+ let mut requires = IndexMap::new();
requires.insert("foo/foo".to_string(), Some("^1.0".to_string()));
let generator = RuleSetGenerator::new(&mut pool);
- let (rules, _) = generator.generate(&requires, &[], &HashMap::new(), &HashMap::new());
+ let (rules, _) = generator.generate(&requires, &[], &IndexMap::new(), &IndexMap::new());
let policy = DefaultPolicy::default();
- let solver = Solver::new(rules, &pool, policy, HashSet::new());
+ let solver = Solver::new(rules, &pool, policy, IndexSet::new());
let result = solver.solve().unwrap();
// Should install foo/foo (id=1) and bar/bar (id=2)
@@ -1335,7 +1335,7 @@ mod tests {
require_dev: vec![],
include_dev: false,
minimum_stability: Stability::Stable,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable: true,
prefer_lowest: false,
platform: PlatformConfig::new(),
@@ -1345,10 +1345,10 @@ mod tests {
std::env::temp_dir().join("mozart-test-cache"),
false,
))),
- temporary_constraints: HashMap::new(),
+ temporary_constraints: IndexMap::new(),
raw_repositories: vec![],
- root_provide: HashMap::new(),
- root_replace: HashMap::new(),
+ root_provide: IndexMap::new(),
+ root_replace: IndexMap::new(),
};
let result = resolve(&request).await;
diff --git a/crates/mozart-registry/src/vcs_bridge.rs b/crates/mozart-registry/src/vcs_bridge.rs
index 1f93a51..e9a2f37 100644
--- a/crates/mozart-registry/src/vcs_bridge.rs
+++ b/crates/mozart-registry/src/vcs_bridge.rs
@@ -3,7 +3,8 @@
//! Scans VCS repositories defined in composer.json and converts
//! discovered package versions into pool inputs for the SAT resolver.
-use std::collections::{BTreeMap, HashMap};
+use indexmap::IndexMap;
+use std::collections::BTreeMap;
use mozart_core::package::{RawRepository, Stability};
use mozart_sat_resolver::{PoolPackageInput, make_pool_links};
@@ -57,7 +58,7 @@ pub async fn scan_vcs_repositories(repositories: &[RawRepository]) -> Vec<VcsPac
pub fn vcs_to_pool_inputs(
vpkg: &VcsPackageVersion,
minimum_stability: Stability,
- stability_flags: &HashMap<String, Stability>,
+ stability_flags: &IndexMap<String, Stability>,
) -> Vec<PoolPackageInput> {
let mut results = Vec::new();
@@ -207,7 +208,7 @@ fn passes_vcs_stability_filter(
package_name: &str,
version: &mozart_semver::Version,
minimum_stability: Stability,
- stability_flags: &HashMap<String, Stability>,
+ stability_flags: &IndexMap<String, Stability>,
) -> bool {
let stability = version_stability(version);
let pkg_flag = stability_flags.get(&package_name.to_lowercase());
diff --git a/crates/mozart-sat-resolver/Cargo.toml b/crates/mozart-sat-resolver/Cargo.toml
index c02904f..5b8a46c 100644
--- a/crates/mozart-sat-resolver/Cargo.toml
+++ b/crates/mozart-sat-resolver/Cargo.toml
@@ -6,5 +6,6 @@ edition.workspace = true
[dependencies]
mozart-semver.workspace = true
mozart-core.workspace = true
+indexmap.workspace = true
[dev-dependencies]
diff --git a/crates/mozart-sat-resolver/src/decisions.rs b/crates/mozart-sat-resolver/src/decisions.rs
index abfbe3d..e9cc935 100644
--- a/crates/mozart-sat-resolver/src/decisions.rs
+++ b/crates/mozart-sat-resolver/src/decisions.rs
@@ -1,7 +1,7 @@
use crate::error::SolverBugError;
use crate::pool::{Literal, PackageId, literal_to_package_id};
use crate::rule_set::RuleId;
-use std::collections::HashMap;
+use indexmap::IndexMap;
/// A decision entry: which literal was decided and which rule caused it.
#[derive(Debug, Clone)]
@@ -16,7 +16,7 @@ pub struct Decision {
pub struct Decisions {
/// Package ID → signed level. Positive = install, negative = uninstall.
/// The absolute value is the decision level.
- decision_map: HashMap<PackageId, i32>,
+ decision_map: IndexMap<PackageId, i32>,
/// Queue of decisions in order.
decision_queue: Vec<Decision>,
}
@@ -24,7 +24,7 @@ pub struct Decisions {
impl Decisions {
pub fn new() -> Self {
Decisions {
- decision_map: HashMap::new(),
+ decision_map: IndexMap::new(),
decision_queue: Vec::new(),
}
}
diff --git a/crates/mozart-sat-resolver/src/policy.rs b/crates/mozart-sat-resolver/src/policy.rs
index a66719f..a2678d5 100644
--- a/crates/mozart-sat-resolver/src/policy.rs
+++ b/crates/mozart-sat-resolver/src/policy.rs
@@ -1,5 +1,5 @@
use crate::pool::{Literal, Pool};
-use std::collections::HashMap;
+use indexmap::IndexMap;
/// Version selection policy: decides which version to prefer when multiple
/// candidates satisfy a requirement.
@@ -35,7 +35,7 @@ impl DefaultPolicy {
}
// Group literals by package name
- let mut groups: HashMap<&str, Vec<Literal>> = HashMap::new();
+ let mut groups: IndexMap<&str, Vec<Literal>> = IndexMap::new();
for &lit in literals {
let pkg = pool.literal_to_package(lit);
groups.entry(pkg.name.as_str()).or_default().push(lit);
diff --git a/crates/mozart-sat-resolver/src/pool.rs b/crates/mozart-sat-resolver/src/pool.rs
index 0312c24..9268675 100644
--- a/crates/mozart-sat-resolver/src/pool.rs
+++ b/crates/mozart-sat-resolver/src/pool.rs
@@ -1,5 +1,5 @@
+use indexmap::IndexMap;
use mozart_semver::VersionConstraint;
-use std::collections::HashMap;
use std::fmt;
/// Unique identifier for a package in the pool. 1-based.
@@ -122,9 +122,9 @@ pub struct Pool {
/// All packages, indexed by (id - 1).
packages: Vec<PoolPackage>,
/// Index: package name → list of package IDs providing that name.
- package_by_name: HashMap<String, Vec<PackageId>>,
+ package_by_name: IndexMap<String, Vec<PackageId>>,
/// Cache for what_provides results.
- provider_cache: HashMap<(String, String), Vec<PackageId>>,
+ provider_cache: IndexMap<(String, String), Vec<PackageId>>,
/// Packages that are fixed/locked but unacceptable (e.g. failed stability).
unacceptable_fixed_packages: Vec<PackageId>,
}
@@ -133,7 +133,7 @@ impl Pool {
/// Create a new pool from a list of package inputs.
pub fn new(inputs: Vec<PoolPackageInput>, unacceptable_fixed_ids: Vec<PackageId>) -> Self {
let mut packages: Vec<PoolPackage> = Vec::with_capacity(inputs.len());
- let mut package_by_name: HashMap<String, Vec<PackageId>> = HashMap::new();
+ let mut package_by_name: IndexMap<String, Vec<PackageId>> = IndexMap::new();
// Collect alias links (alias_idx, target_name, target_normalized) for
// a second pass once every input has a stable ID.
let mut pending_aliases: Vec<(usize, String, String)> = Vec::new();
@@ -189,7 +189,7 @@ impl Pool {
Pool {
packages,
package_by_name,
- provider_cache: HashMap::new(),
+ provider_cache: IndexMap::new(),
unacceptable_fixed_packages: unacceptable_fixed_ids,
}
}
diff --git a/crates/mozart-sat-resolver/src/pool_builder.rs b/crates/mozart-sat-resolver/src/pool_builder.rs
index 83684aa..3883d85 100644
--- a/crates/mozart-sat-resolver/src/pool_builder.rs
+++ b/crates/mozart-sat-resolver/src/pool_builder.rs
@@ -1,5 +1,6 @@
use crate::pool::{Pool, PoolLink, PoolPackageInput};
-use std::collections::{HashSet, VecDeque};
+use indexmap::IndexSet;
+use std::collections::VecDeque;
/// Builder for constructing a Pool from package metadata.
///
@@ -10,13 +11,13 @@ pub struct PoolBuilder {
/// Packages to add to the pool.
inputs: Vec<PoolPackageInput>,
/// Names already added (to avoid duplicates).
- added: HashSet<String>,
+ added: IndexSet<String>,
/// Queue of package names that need to be explored.
pending_names: VecDeque<String>,
/// Package names that have already been explored (returned by next_pending).
- explored_names: HashSet<String>,
+ explored_names: IndexSet<String>,
/// Specific platform packages to ignore (from `--ignore-platform-req=name`).
- ignore_platform_reqs: HashSet<String>,
+ ignore_platform_reqs: IndexSet<String>,
/// When true, ignore every platform package (php, ext-*, lib-*, composer-*).
/// Mirrors `--ignore-platform-reqs` (no value).
ignore_all_platform_reqs: bool,
@@ -26,16 +27,16 @@ impl PoolBuilder {
pub fn new() -> Self {
PoolBuilder {
inputs: Vec::new(),
- added: HashSet::new(),
+ added: IndexSet::new(),
pending_names: VecDeque::new(),
- explored_names: HashSet::new(),
- ignore_platform_reqs: HashSet::new(),
+ explored_names: IndexSet::new(),
+ ignore_platform_reqs: IndexSet::new(),
ignore_all_platform_reqs: false,
}
}
/// Set platform requirements to ignore during exploration.
- pub fn set_ignore_platform_reqs(&mut self, names: HashSet<String>) {
+ pub fn set_ignore_platform_reqs(&mut self, names: IndexSet<String>) {
self.ignore_platform_reqs = names;
}
diff --git a/crates/mozart-sat-resolver/src/problem.rs b/crates/mozart-sat-resolver/src/problem.rs
index c453fa9..a1692fd 100644
--- a/crates/mozart-sat-resolver/src/problem.rs
+++ b/crates/mozart-sat-resolver/src/problem.rs
@@ -75,7 +75,7 @@ impl Problem {
}
// Deduplicate
- let mut seen = std::collections::HashSet::new();
+ let mut seen = indexmap::IndexSet::new();
let mut unique = Vec::new();
for msg in messages {
if seen.insert(msg.clone()) {
@@ -367,7 +367,7 @@ fn rule_pretty_string(pool: &Pool, rule: &Rule) -> String {
/// Similar to Composer's formatPackagesUnique.
fn format_providers(pool: &Pool, literals: &[Literal]) -> String {
// Group by package name
- let mut groups: std::collections::HashMap<&str, Vec<&str>> = std::collections::HashMap::new();
+ let mut groups: indexmap::IndexMap<&str, Vec<&str>> = indexmap::IndexMap::new();
for &lit in literals {
let pkg = pool.literal_to_package(lit);
groups
diff --git a/crates/mozart-sat-resolver/src/request.rs b/crates/mozart-sat-resolver/src/request.rs
index 94891f0..26c17ba 100644
--- a/crates/mozart-sat-resolver/src/request.rs
+++ b/crates/mozart-sat-resolver/src/request.rs
@@ -1,5 +1,5 @@
use crate::pool::PackageId;
-use std::collections::HashMap;
+use indexmap::IndexMap;
/// A requirement: package name + version constraint string.
#[derive(Debug, Clone)]
@@ -14,7 +14,7 @@ pub struct Require {
#[derive(Debug, Clone)]
pub struct Request {
/// Root requirements: package name → constraint string.
- pub requires: HashMap<String, Option<String>>,
+ pub requires: IndexMap<String, Option<String>>,
/// Fixed packages (must be installed, cannot be modified).
pub fixed_packages: Vec<PackageId>,
/// Locked packages (installed but can be removed if nothing requires them).
@@ -24,7 +24,7 @@ pub struct Request {
impl Request {
pub fn new() -> Self {
Request {
- requires: HashMap::new(),
+ requires: IndexMap::new(),
fixed_packages: Vec::new(),
locked_packages: Vec::new(),
}
diff --git a/crates/mozart-sat-resolver/src/rule_set.rs b/crates/mozart-sat-resolver/src/rule_set.rs
index 4d1a8a6..918bdae 100644
--- a/crates/mozart-sat-resolver/src/rule_set.rs
+++ b/crates/mozart-sat-resolver/src/rule_set.rs
@@ -1,5 +1,5 @@
use crate::rule::{Rule, RuleType};
-use std::collections::HashMap;
+use indexmap::IndexMap;
/// A unique identifier for a rule within the RuleSet.
pub type RuleId = usize;
@@ -18,7 +18,7 @@ pub struct RuleSet {
/// Total rule count.
next_rule_id: usize,
/// Deduplication index.
- rules_by_hash: HashMap<String, Vec<usize>>,
+ rules_by_hash: IndexMap<String, Vec<usize>>,
/// Maps rule ID → (type, index within type's vec).
rule_type_index: Vec<(RuleType, usize)>,
}
@@ -31,7 +31,7 @@ impl RuleSet {
request_rules: Vec::new(),
learned_rules: Vec::new(),
next_rule_id: 0,
- rules_by_hash: HashMap::new(),
+ rules_by_hash: IndexMap::new(),
rule_type_index: Vec::new(),
}
}
diff --git a/crates/mozart-sat-resolver/src/rule_set_generator.rs b/crates/mozart-sat-resolver/src/rule_set_generator.rs
index 11c04cc..2ab9f86 100644
--- a/crates/mozart-sat-resolver/src/rule_set_generator.rs
+++ b/crates/mozart-sat-resolver/src/rule_set_generator.rs
@@ -1,8 +1,10 @@
use crate::pool::{Literal, PackageId, Pool, PoolLink};
use crate::rule::{ReasonData, Rule, RuleReason, RuleType};
use crate::rule_set::RuleSet;
+use indexmap::IndexMap;
+use indexmap::IndexSet;
use mozart_semver::VersionConstraint;
-use std::collections::{HashMap, HashSet, VecDeque};
+use std::collections::VecDeque;
/// Generates SAT rules from the pool and request.
///
@@ -11,11 +13,11 @@ pub struct RuleSetGenerator<'a> {
pool: &'a mut Pool,
rules: RuleSet,
/// Packages already processed.
- added_map: HashSet<PackageId>,
+ added_map: IndexSet<PackageId>,
/// Package names → list of package IDs with that name (non-alias).
- added_packages_by_name: HashMap<String, Vec<PackageId>>,
+ added_packages_by_name: IndexMap<String, Vec<PackageId>>,
/// Specific platform packages to ignore (from `--ignore-platform-req=name`).
- ignore_platform_reqs: HashSet<String>,
+ ignore_platform_reqs: IndexSet<String>,
/// When true, every platform package is treated as ignored.
/// Mirrors `--ignore-platform-reqs` (no value).
ignore_all_platform_reqs: bool,
@@ -26,15 +28,15 @@ impl<'a> RuleSetGenerator<'a> {
RuleSetGenerator {
pool,
rules: RuleSet::new(),
- added_map: HashSet::new(),
- added_packages_by_name: HashMap::new(),
- ignore_platform_reqs: HashSet::new(),
+ added_map: IndexSet::new(),
+ added_packages_by_name: IndexMap::new(),
+ ignore_platform_reqs: IndexSet::new(),
ignore_all_platform_reqs: false,
}
}
/// Set platform requirements to ignore.
- pub fn set_ignore_platform_reqs(&mut self, names: HashSet<String>) {
+ pub fn set_ignore_platform_reqs(&mut self, names: IndexSet<String>) {
self.ignore_platform_reqs = names;
}
@@ -76,10 +78,10 @@ impl<'a> RuleSetGenerator<'a> {
/// unresolvable problem.
pub fn generate(
mut self,
- requires: &HashMap<String, Option<String>>,
+ requires: &IndexMap<String, Option<String>>,
fixed_packages: &[PackageId],
- root_provides: &HashMap<String, String>,
- root_replaces: &HashMap<String, String>,
+ root_provides: &IndexMap<String, String>,
+ root_replaces: &IndexMap<String, String>,
) -> (RuleSet, Vec<(String, Option<String>)>) {
let mut missing_root_requires: Vec<(String, Option<String>)> = Vec::new();
// Process fixed packages
@@ -351,7 +353,7 @@ impl<'a> RuleSetGenerator<'a> {
fn root_self_fulfills(
target: &str,
require_constraint: Option<&str>,
- root_links: &HashMap<String, String>,
+ root_links: &IndexMap<String, String>,
) -> bool {
let Some(link_constraint_str) = root_links.get(target) else {
return false;
@@ -394,11 +396,11 @@ mod tests {
vec![],
);
- let mut requires = HashMap::new();
+ let mut requires = IndexMap::new();
requires.insert("a/a".to_string(), None);
let generator = RuleSetGenerator::new(&mut pool);
- let (rules, _) = generator.generate(&requires, &[], &HashMap::new(), &HashMap::new());
+ let (rules, _) = generator.generate(&requires, &[], &IndexMap::new(), &IndexMap::new());
// Should have a request rule: (1 | 2)
let request_count = rules.iter_type(RuleType::Request).count();
@@ -434,11 +436,11 @@ mod tests {
vec![],
);
- let mut requires = HashMap::new();
+ let mut requires = IndexMap::new();
requires.insert("a/a".to_string(), None);
let generator = RuleSetGenerator::new(&mut pool);
- let (rules, _) = generator.generate(&requires, &[], &HashMap::new(), &HashMap::new());
+ let (rules, _) = generator.generate(&requires, &[], &IndexMap::new(), &IndexMap::new());
// Should have:
// 1. Request rule: (1) — root requires a/a
@@ -452,7 +454,7 @@ mod tests {
let generator = RuleSetGenerator::new(&mut pool);
let (rules, _) =
- generator.generate(&HashMap::new(), &[1], &HashMap::new(), &HashMap::new());
+ generator.generate(&IndexMap::new(), &[1], &IndexMap::new(), &IndexMap::new());
// Should have an assertion rule: (1)
let request_rules: Vec<_> = rules.iter_type(RuleType::Request).collect();
diff --git a/crates/mozart-sat-resolver/src/rule_watch_graph.rs b/crates/mozart-sat-resolver/src/rule_watch_graph.rs
index 1b7604d..202dcca 100644
--- a/crates/mozart-sat-resolver/src/rule_watch_graph.rs
+++ b/crates/mozart-sat-resolver/src/rule_watch_graph.rs
@@ -2,7 +2,7 @@ use crate::decisions::Decisions;
use crate::pool::Literal;
use crate::rule::Rule;
use crate::rule_set::RuleId;
-use std::collections::HashMap;
+use indexmap::IndexMap;
/// A watch node: tracks which 2 literals a rule watches.
///
@@ -24,7 +24,7 @@ struct WatchNode {
/// Port of Composer's RuleWatchGraph.php.
pub struct RuleWatchGraph {
/// Literal → list of watch node indices watching that literal.
- watch_chains: HashMap<Literal, Vec<usize>>,
+ watch_chains: IndexMap<Literal, Vec<usize>>,
/// All watch nodes.
nodes: Vec<WatchNode>,
}
@@ -32,7 +32,7 @@ pub struct RuleWatchGraph {
impl RuleWatchGraph {
pub fn new() -> Self {
RuleWatchGraph {
- watch_chains: HashMap::new(),
+ watch_chains: IndexMap::new(),
nodes: Vec::new(),
}
}
diff --git a/crates/mozart-sat-resolver/src/solver.rs b/crates/mozart-sat-resolver/src/solver.rs
index 49a4ce4..8739381 100644
--- a/crates/mozart-sat-resolver/src/solver.rs
+++ b/crates/mozart-sat-resolver/src/solver.rs
@@ -6,7 +6,7 @@ use crate::problem::Problem;
use crate::rule::{ReasonData, Rule, RuleReason, RuleType};
use crate::rule_set::{RuleId, RuleSet};
use crate::rule_watch_graph::RuleWatchGraph;
-use std::collections::{HashMap, HashSet};
+use indexmap::{IndexMap, IndexSet};
/// Result of solving: the list of package IDs to install.
#[derive(Debug)]
@@ -25,7 +25,7 @@ pub struct Solver<'a> {
watch_graph: RuleWatchGraph,
decisions: Decisions,
/// Fixed packages by ID.
- fixed_map: HashSet<PackageId>,
+ fixed_map: IndexSet<PackageId>,
/// Current propagation index in decision queue.
propagate_index: usize,
/// Branch points: (alternative literals, decision level).
@@ -35,7 +35,7 @@ pub struct Solver<'a> {
/// Learned rule pool: for each learned rule, the chain of rules that led to it.
learned_pool: Vec<Vec<RuleId>>,
/// Map from rule ID → learned pool index.
- learned_why: HashMap<RuleId, usize>,
+ learned_why: IndexMap<RuleId, usize>,
}
impl<'a> Solver<'a> {
@@ -44,7 +44,7 @@ impl<'a> Solver<'a> {
rules: RuleSet,
pool: &'a Pool,
policy: DefaultPolicy,
- fixed_packages: HashSet<PackageId>,
+ fixed_packages: IndexSet<PackageId>,
) -> Self {
Solver {
pool,
@@ -57,7 +57,7 @@ impl<'a> Solver<'a> {
branches: Vec::new(),
problems: Vec::new(),
learned_pool: Vec::new(),
- learned_why: HashMap::new(),
+ learned_why: IndexMap::new(),
}
}
@@ -333,7 +333,7 @@ impl<'a> Solver<'a> {
let mut rule_level: i32 = 1;
let mut num: i32 = 0;
let mut l1num: i32 = 0;
- let mut seen: HashSet<PackageId> = HashSet::new();
+ let mut seen: IndexSet<PackageId> = IndexSet::new();
let mut learned_literal: Option<Literal> = None;
let mut other_learned_literals: Vec<Literal> = Vec::new();
@@ -430,7 +430,7 @@ impl<'a> Solver<'a> {
let decision = self.decisions.at_offset(decision_id);
let literal = decision.literal;
- seen.remove(&literal_to_package_id(literal));
+ seen.shift_remove(&literal_to_package_id(literal));
if num != 0 {
num -= 1;
@@ -456,7 +456,7 @@ impl<'a> Solver<'a> {
// Only level 1 marks left
for other in &other_learned_literals {
- seen.remove(&literal_to_package_id(*other));
+ seen.shift_remove(&literal_to_package_id(*other));
}
l1num += 1;
l1retry = true;
@@ -504,7 +504,7 @@ impl<'a> Solver<'a> {
&self,
problem: &mut Problem,
conflict_rule_id: RuleId,
- rule_seen: &mut HashSet<RuleId>,
+ rule_seen: &mut IndexSet<RuleId>,
) {
if rule_seen.contains(&conflict_rule_id) {
return;
@@ -542,11 +542,11 @@ impl<'a> Solver<'a> {
let mut problem = Problem::new();
problem.add_rule(conflict_rule_id);
- let mut rule_seen = HashSet::new();
+ let mut rule_seen = IndexSet::new();
self.analyze_unsolvable_rule(&mut problem, conflict_rule_id, &mut rule_seen);
// Collect related decisions
- let mut seen: HashSet<PackageId> = HashSet::new();
+ let mut seen: IndexSet<PackageId> = IndexSet::new();
let conflict_literals = self.rules.rule_by_id(conflict_rule_id).literals().to_vec();
for &lit in &conflict_literals {
if self.decisions.satisfy(lit) {
@@ -835,7 +835,7 @@ mod tests {
/// Creates a pool with N dummy packages (1..=max_id).
fn make_rules_and_solve(
rules: Vec<(Rule, RuleType)>,
- fixed: HashSet<PackageId>,
+ fixed: IndexSet<PackageId>,
max_id: u32,
) -> Result<SolverResult, SolverError> {
let mut rs = RuleSet::new();
@@ -859,7 +859,7 @@ mod tests {
Rule::new(vec![1], RuleReason::RootRequire, ReasonData::None),
RuleType::Request,
)],
- HashSet::new(),
+ IndexSet::new(),
3,
)
.unwrap();
@@ -881,7 +881,7 @@ mod tests {
RuleType::Request,
),
],
- HashSet::new(),
+ IndexSet::new(),
3,
)
.unwrap();
@@ -907,7 +907,7 @@ mod tests {
RuleType::Package,
),
],
- HashSet::new(),
+ IndexSet::new(),
3,
)
.unwrap();
@@ -944,7 +944,7 @@ mod tests {
RuleType::Request,
),
],
- HashSet::new(),
+ IndexSet::new(),
3,
)
.unwrap();
@@ -970,7 +970,7 @@ mod tests {
RuleType::Package,
),
],
- HashSet::new(),
+ IndexSet::new(),
3,
)
.unwrap();
@@ -999,7 +999,7 @@ mod tests {
RuleType::Package,
),
],
- HashSet::new(),
+ IndexSet::new(),
3,
);
diff --git a/crates/mozart-sat-resolver/src/transaction.rs b/crates/mozart-sat-resolver/src/transaction.rs
index 176b862..de7c9a0 100644
--- a/crates/mozart-sat-resolver/src/transaction.rs
+++ b/crates/mozart-sat-resolver/src/transaction.rs
@@ -1,6 +1,6 @@
use crate::decisions::Decisions;
use crate::pool::{PackageId, Pool, literal_to_package_id};
-use std::collections::{HashMap, HashSet};
+use indexmap::{IndexMap, IndexSet};
/// An operation to perform on a package.
///
@@ -104,14 +104,14 @@ impl<'a> Transaction<'a> {
/// Calculate the delta between present and result packages.
fn calculate_operations(&mut self) {
// Build maps: name -> package_id for present packages
- let mut present_by_name: HashMap<&str, PackageId> = HashMap::new();
+ let mut present_by_name: IndexMap<&str, PackageId> = IndexMap::new();
for &id in &self.present_ids {
let pkg = self.pool.package_by_id(id);
present_by_name.insert(&pkg.name, id);
}
// Track which present packages have been matched
- let mut matched_present: HashSet<PackageId> = HashSet::new();
+ let mut matched_present: IndexSet<PackageId> = IndexSet::new();
// Build topologically sorted result packages via DFS
let sorted_results = self.topological_sort();
@@ -158,9 +158,9 @@ impl<'a> Transaction<'a> {
/// Topologically sort result packages by their dependency order.
/// Uses DFS: dependencies are processed before dependents.
fn topological_sort(&self) -> Vec<PackageId> {
- let result_set: HashSet<PackageId> = self.result_ids.iter().copied().collect();
- let result_by_name: HashMap<&str, Vec<PackageId>> = {
- let mut map: HashMap<&str, Vec<PackageId>> = HashMap::new();
+ let result_set: IndexSet<PackageId> = self.result_ids.iter().copied().collect();
+ let result_by_name: IndexMap<&str, Vec<PackageId>> = {
+ let mut map: IndexMap<&str, Vec<PackageId>> = IndexMap::new();
for &id in &self.result_ids {
let pkg = self.pool.package_by_id(id);
map.entry(&pkg.name).or_default().push(id);
@@ -168,7 +168,7 @@ impl<'a> Transaction<'a> {
map
};
- let mut visited: HashSet<PackageId> = HashSet::new();
+ let mut visited: IndexSet<PackageId> = IndexSet::new();
let mut order: Vec<PackageId> = Vec::new();
// Find root packages (not required by any other result package)
@@ -221,11 +221,11 @@ impl<'a> Transaction<'a> {
/// Find root packages: result packages not required by any other result package.
fn get_root_packages(
&self,
- result_set: &HashSet<PackageId>,
- _result_by_name: &HashMap<&str, Vec<PackageId>>,
+ result_set: &IndexSet<PackageId>,
+ _result_by_name: &IndexMap<&str, Vec<PackageId>>,
) -> Vec<PackageId> {
// Collect all required package names
- let mut required_names: HashSet<&str> = HashSet::new();
+ let mut required_names: IndexSet<&str> = IndexSet::new();
for &id in result_set {
let pkg = self.pool.package_by_id(id);
for req in &pkg.requires {
@@ -270,7 +270,7 @@ impl<'a> LockTransaction<'a> {
pub fn new(
pool: &'a Pool,
present_ids: Vec<PackageId>,
- unlockable_ids: HashSet<PackageId>,
+ unlockable_ids: IndexSet<PackageId>,
decisions: &Decisions,
) -> Self {
// Extract result packages from decisions
@@ -300,7 +300,7 @@ impl<'a> LockTransaction<'a> {
/// Set the non-dev packages from an extraction-only solve result.
/// `extraction_ids` are the package IDs that were resolved without dev deps.
pub fn set_non_dev_packages(&mut self, extraction_ids: &[PackageId]) {
- let extraction_names: HashSet<String> = extraction_ids
+ let extraction_names: IndexSet<String> = extraction_ids
.iter()
.map(|&id| self.transaction.pool.package_by_id(id).name.clone())
.collect();
diff --git a/crates/mozart-spdx-licenses/Cargo.toml b/crates/mozart-spdx-licenses/Cargo.toml
index fc31b9f..1500932 100644
--- a/crates/mozart-spdx-licenses/Cargo.toml
+++ b/crates/mozart-spdx-licenses/Cargo.toml
@@ -4,6 +4,7 @@ version.workspace = true
edition.workspace = true
[dependencies]
+indexmap.workspace = true
[build-dependencies]
serde.workspace = true
diff --git a/crates/mozart-spdx-licenses/src/lib.rs b/crates/mozart-spdx-licenses/src/lib.rs
index 81fa329..668270f 100644
--- a/crates/mozart-spdx-licenses/src/lib.rs
+++ b/crates/mozart-spdx-licenses/src/lib.rs
@@ -1,4 +1,4 @@
-use std::collections::HashMap;
+use indexmap::IndexMap;
use std::sync::LazyLock;
include!(concat!(env!("OUT_DIR"), "/spdx_data.rs"));
@@ -21,16 +21,16 @@ pub struct ExceptionInfo {
/// SPDX license database with expression validation.
pub struct SpdxLicenses {
- licenses: HashMap<&'static str, LicenseInfo>,
- exceptions: HashMap<&'static str, ExceptionInfo>,
- name_to_id: HashMap<&'static str, &'static str>,
+ licenses: IndexMap<&'static str, LicenseInfo>,
+ exceptions: IndexMap<&'static str, ExceptionInfo>,
+ name_to_id: IndexMap<&'static str, &'static str>,
}
impl SpdxLicenses {
/// Build the license database from generated data.
pub fn new() -> Self {
- let mut licenses = HashMap::with_capacity(LICENSES.len());
- let mut name_to_id = HashMap::with_capacity(LICENSES.len());
+ let mut licenses = IndexMap::with_capacity(LICENSES.len());
+ let mut name_to_id = IndexMap::with_capacity(LICENSES.len());
for &(lower, id, full_name, osi, deprecated) in LICENSES {
licenses.insert(
lower,
@@ -44,7 +44,7 @@ impl SpdxLicenses {
name_to_id.insert(full_name, id);
}
- let mut exceptions = HashMap::with_capacity(EXCEPTIONS.len());
+ let mut exceptions = IndexMap::with_capacity(EXCEPTIONS.len());
for &(lower, id, full_name) in EXCEPTIONS {
exceptions.insert(
lower,
diff --git a/crates/mozart-test-harness/Cargo.toml b/crates/mozart-test-harness/Cargo.toml
index 61b9109..6c9421c 100644
--- a/crates/mozart-test-harness/Cargo.toml
+++ b/crates/mozart-test-harness/Cargo.toml
@@ -5,6 +5,7 @@ edition.workspace = true
[dependencies]
anyhow.workspace = true
+indexmap.workspace = true
regex.workspace = true
serde_json.workspace = true
tempfile.workspace = true
diff --git a/crates/mozart-test-harness/src/parser.rs b/crates/mozart-test-harness/src/parser.rs
index dbc71ae..827272b 100644
--- a/crates/mozart-test-harness/src/parser.rs
+++ b/crates/mozart-test-harness/src/parser.rs
@@ -1,5 +1,5 @@
use anyhow::{Context, Result, bail};
-use std::collections::HashMap;
+use indexmap::IndexMap;
use std::fs;
use std::path::Path;
@@ -53,7 +53,7 @@ pub fn parse_test_str(content: &str) -> Result<ParsedTest> {
}
}
- let mut take = |key: &str| sections.remove(key);
+ let mut take = |key: &str| sections.shift_remove(key);
let test = take("TEST").unwrap();
let composer = take("COMPOSER").unwrap();
@@ -86,10 +86,10 @@ pub fn parse_test_str(content: &str) -> Result<ParsedTest> {
})
}
-fn split_sections(content: &str) -> Result<HashMap<String, String>> {
+fn split_sections(content: &str) -> Result<IndexMap<String, String>> {
let header_re = regex::Regex::new(r"^--([A-Z][A-Z-]*)--$").unwrap();
- let mut sections: HashMap<String, String> = HashMap::new();
+ let mut sections: IndexMap<String, String> = IndexMap::new();
let mut current_section: Option<String> = None;
let mut current_body = String::new();
diff --git a/crates/mozart-vcs/Cargo.toml b/crates/mozart-vcs/Cargo.toml
index dc6dfc3..18eff25 100644
--- a/crates/mozart-vcs/Cargo.toml
+++ b/crates/mozart-vcs/Cargo.toml
@@ -8,6 +8,7 @@ mozart-core.workspace = true
mozart-semver.workspace = true
anyhow.workspace = true
base64.workspace = true
+indexmap.workspace = true
regex.workspace = true
reqwest.workspace = true
serde.workspace = true
diff --git a/crates/mozart-vcs/src/driver/bitbucket.rs b/crates/mozart-vcs/src/driver/bitbucket.rs
index d47987d..77704fa 100644
--- a/crates/mozart-vcs/src/driver/bitbucket.rs
+++ b/crates/mozart-vcs/src/driver/bitbucket.rs
@@ -1,4 +1,5 @@
-use std::collections::{BTreeMap, HashMap};
+use indexmap::IndexMap;
+use std::collections::BTreeMap;
use anyhow::{Result, bail};
use regex::Regex;
@@ -16,7 +17,7 @@ pub struct BitbucketDriver {
root_identifier: Option<String>,
tags: Option<BTreeMap<String, String>>,
branches: Option<BTreeMap<String, String>>,
- info_cache: HashMap<String, Option<serde_json::Value>>,
+ info_cache: IndexMap<String, Option<serde_json::Value>>,
git_driver: Option<Box<GitDriver>>,
http_client: Client,
config: DriverConfig,
@@ -34,7 +35,7 @@ impl BitbucketDriver {
root_identifier: None,
tags: None,
branches: None,
- info_cache: HashMap::new(),
+ info_cache: IndexMap::new(),
git_driver: None,
http_client: Client::new(),
config,
diff --git a/crates/mozart-vcs/src/driver/forgejo.rs b/crates/mozart-vcs/src/driver/forgejo.rs
index ec2ca14..488e165 100644
--- a/crates/mozart-vcs/src/driver/forgejo.rs
+++ b/crates/mozart-vcs/src/driver/forgejo.rs
@@ -1,4 +1,5 @@
-use std::collections::{BTreeMap, HashMap};
+use indexmap::IndexMap;
+use std::collections::BTreeMap;
use anyhow::{Result, bail};
use regex::Regex;
@@ -20,7 +21,7 @@ pub struct ForgejoDriver {
root_identifier: Option<String>,
tags: Option<BTreeMap<String, String>>,
branches: Option<BTreeMap<String, String>>,
- info_cache: HashMap<String, Option<serde_json::Value>>,
+ info_cache: IndexMap<String, Option<serde_json::Value>>,
git_driver: Option<Box<GitDriver>>,
http_client: Client,
config: DriverConfig,
@@ -39,7 +40,7 @@ impl ForgejoDriver {
root_identifier: None,
tags: None,
branches: None,
- info_cache: HashMap::new(),
+ info_cache: IndexMap::new(),
git_driver: None,
http_client: Client::new(),
config,
diff --git a/crates/mozart-vcs/src/driver/git.rs b/crates/mozart-vcs/src/driver/git.rs
index cc9a210..43f4ecb 100644
--- a/crates/mozart-vcs/src/driver/git.rs
+++ b/crates/mozart-vcs/src/driver/git.rs
@@ -1,4 +1,5 @@
-use std::collections::{BTreeMap, HashMap};
+use indexmap::IndexMap;
+use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use anyhow::Result;
@@ -17,7 +18,7 @@ pub struct GitDriver {
root_identifier: Option<String>,
tags: Option<BTreeMap<String, String>>,
branches: Option<BTreeMap<String, String>>,
- info_cache: HashMap<String, Option<serde_json::Value>>,
+ info_cache: IndexMap<String, Option<serde_json::Value>>,
git_util: GitUtil,
is_local: bool,
}
@@ -37,7 +38,7 @@ impl GitDriver {
root_identifier: None,
tags: None,
branches: None,
- info_cache: HashMap::new(),
+ info_cache: IndexMap::new(),
git_util,
is_local,
}
@@ -85,7 +86,7 @@ impl GitDriver {
fn parse_tags(output: &str) -> BTreeMap<String, String> {
let mut tags = BTreeMap::new();
// First pass: collect dereferenced tags (^{})
- let mut dereferenced = HashMap::new();
+ let mut dereferenced = IndexMap::new();
for line in output.lines() {
let line = line.trim();
if line.is_empty() {
diff --git a/crates/mozart-vcs/src/driver/github.rs b/crates/mozart-vcs/src/driver/github.rs
index c47c2fe..9c11389 100644
--- a/crates/mozart-vcs/src/driver/github.rs
+++ b/crates/mozart-vcs/src/driver/github.rs
@@ -1,4 +1,5 @@
-use std::collections::{BTreeMap, HashMap};
+use indexmap::IndexMap;
+use std::collections::BTreeMap;
use anyhow::{Result, bail};
use regex::Regex;
@@ -19,7 +20,7 @@ pub struct GitHubDriver {
tags: Option<BTreeMap<String, String>>,
branches: Option<BTreeMap<String, String>>,
repo_data: Option<serde_json::Value>,
- info_cache: HashMap<String, Option<serde_json::Value>>,
+ info_cache: IndexMap<String, Option<serde_json::Value>>,
git_driver: Option<Box<GitDriver>>,
http_client: Client,
config: DriverConfig,
@@ -37,7 +38,7 @@ impl GitHubDriver {
tags: None,
branches: None,
repo_data: None,
- info_cache: HashMap::new(),
+ info_cache: IndexMap::new(),
git_driver: None,
http_client: Client::new(),
config,
diff --git a/crates/mozart-vcs/src/driver/gitlab.rs b/crates/mozart-vcs/src/driver/gitlab.rs
index f96c078..c1afbcb 100644
--- a/crates/mozart-vcs/src/driver/gitlab.rs
+++ b/crates/mozart-vcs/src/driver/gitlab.rs
@@ -1,4 +1,5 @@
-use std::collections::{BTreeMap, HashMap};
+use indexmap::IndexMap;
+use std::collections::BTreeMap;
use anyhow::{Result, bail};
use regex::Regex;
@@ -21,7 +22,7 @@ pub struct GitLabDriver {
root_identifier: Option<String>,
tags: Option<BTreeMap<String, String>>,
branches: Option<BTreeMap<String, String>>,
- info_cache: HashMap<String, Option<serde_json::Value>>,
+ info_cache: IndexMap<String, Option<serde_json::Value>>,
git_driver: Option<Box<GitDriver>>,
http_client: Client,
config: DriverConfig,
@@ -41,7 +42,7 @@ impl GitLabDriver {
root_identifier: None,
tags: None,
branches: None,
- info_cache: HashMap::new(),
+ info_cache: IndexMap::new(),
git_driver: None,
http_client: Client::new(),
config,
diff --git a/crates/mozart-vcs/src/driver/hg.rs b/crates/mozart-vcs/src/driver/hg.rs
index f884c50..0782775 100644
--- a/crates/mozart-vcs/src/driver/hg.rs
+++ b/crates/mozart-vcs/src/driver/hg.rs
@@ -1,4 +1,5 @@
-use std::collections::{BTreeMap, HashMap};
+use indexmap::IndexMap;
+use std::collections::BTreeMap;
use std::path::PathBuf;
use anyhow::Result;
@@ -17,7 +18,7 @@ pub struct HgDriver {
root_identifier: Option<String>,
tags: Option<BTreeMap<String, String>>,
branches: Option<BTreeMap<String, String>>,
- info_cache: HashMap<String, Option<serde_json::Value>>,
+ info_cache: IndexMap<String, Option<serde_json::Value>>,
hg_util: HgUtil,
config: DriverConfig,
}
@@ -31,7 +32,7 @@ impl HgDriver {
root_identifier: None,
tags: None,
branches: None,
- info_cache: HashMap::new(),
+ info_cache: IndexMap::new(),
hg_util: HgUtil::new(process),
config,
}
diff --git a/crates/mozart-vcs/src/driver/svn.rs b/crates/mozart-vcs/src/driver/svn.rs
index eea2d08..16363e1 100644
--- a/crates/mozart-vcs/src/driver/svn.rs
+++ b/crates/mozart-vcs/src/driver/svn.rs
@@ -1,4 +1,5 @@
-use std::collections::{BTreeMap, HashMap};
+use indexmap::IndexMap;
+use std::collections::BTreeMap;
use anyhow::Result;
use regex::Regex;
@@ -20,7 +21,7 @@ pub struct SvnDriver {
root_identifier: Option<String>,
tags: Option<BTreeMap<String, String>>,
branches: Option<BTreeMap<String, String>>,
- info_cache: HashMap<String, Option<serde_json::Value>>,
+ info_cache: IndexMap<String, Option<serde_json::Value>>,
svn_util: SvnUtil,
}
@@ -36,7 +37,7 @@ impl SvnDriver {
root_identifier: None,
tags: None,
branches: None,
- info_cache: HashMap::new(),
+ info_cache: IndexMap::new(),
svn_util: SvnUtil::new(process),
}
}
diff --git a/crates/mozart-vcs/src/process.rs b/crates/mozart-vcs/src/process.rs
index 91741a8..8ccc11d 100644
--- a/crates/mozart-vcs/src/process.rs
+++ b/crates/mozart-vcs/src/process.rs
@@ -1,4 +1,4 @@
-use std::collections::HashMap;
+use indexmap::IndexMap;
use std::path::Path;
use std::process::Command;
use std::time::{Duration, Instant};
@@ -18,7 +18,7 @@ pub struct ProcessOutput {
/// Corresponds to Composer's `ProcessExecutor`.
pub struct ProcessExecutor {
timeout: Option<Duration>,
- env_overrides: HashMap<String, Option<String>>,
+ env_overrides: IndexMap<String, Option<String>>,
}
impl Default for ProcessExecutor {
@@ -31,14 +31,14 @@ impl ProcessExecutor {
pub fn new() -> Self {
Self {
timeout: None,
- env_overrides: HashMap::new(),
+ env_overrides: IndexMap::new(),
}
}
pub fn with_timeout(secs: u64) -> Self {
Self {
timeout: Some(Duration::from_secs(secs)),
- env_overrides: HashMap::new(),
+ env_overrides: IndexMap::new(),
}
}
diff --git a/crates/mozart/Cargo.toml b/crates/mozart/Cargo.toml
index fb47195..bb052dc 100644
--- a/crates/mozart/Cargo.toml
+++ b/crates/mozart/Cargo.toml
@@ -14,6 +14,7 @@ anyhow.workspace = true
clap.workspace = true
clap_complete.workspace = true
colored.workspace = true
+indexmap.workspace = true
regex.workspace = true
reqwest.workspace = true
self-replace.workspace = true
diff --git a/crates/mozart/src/commands/audit.rs b/crates/mozart/src/commands/audit.rs
index 9cbff31..163a43a 100644
--- a/crates/mozart/src/commands/audit.rs
+++ b/crates/mozart/src/commands/audit.rs
@@ -187,7 +187,7 @@ fn load_installed_packages(working_dir: &Path, no_dev: bool) -> anyhow::Result<V
let vendor_dir = working_dir.join("vendor");
let installed = mozart_registry::installed::InstalledPackages::read(&vendor_dir)?;
- let dev_names: std::collections::HashSet<String> = installed
+ let dev_names: indexmap::IndexSet<String> = installed
.dev_package_names
.iter()
.map(|n| n.to_lowercase())
@@ -257,7 +257,7 @@ fn filter_advisories(
ignore_severity: &[String],
console: &mozart_core::console::Console,
) -> BTreeMap<String, Vec<MatchedAdvisory>> {
- let ignore_set: std::collections::HashSet<String> =
+ let ignore_set: indexmap::IndexSet<String> =
ignore_severity.iter().map(|s| s.to_lowercase()).collect();
let mut result: BTreeMap<String, Vec<MatchedAdvisory>> = BTreeMap::new();
diff --git a/crates/mozart/src/commands/bump.rs b/crates/mozart/src/commands/bump.rs
index 60b055f..3ad6ed6 100644
--- a/crates/mozart/src/commands/bump.rs
+++ b/crates/mozart/src/commands/bump.rs
@@ -1,7 +1,7 @@
use clap::Args;
+use indexmap::IndexMap;
use mozart_core::console::Verbosity;
use mozart_core::console_format;
-use std::collections::HashMap;
use std::path::PathBuf;
/// Exit code for stale lock file (matches Composer's BumpCommand::ERROR_LOCK_OUTDATED)
@@ -226,8 +226,8 @@ pub async fn execute(
/// Build a map of lowercase package names to (pretty_version, version_normalized) from composer.lock.
fn build_locked_versions_map(
lock: &mozart_registry::lockfile::LockFile,
-) -> HashMap<String, (String, Option<String>)> {
- let mut map: HashMap<String, (String, Option<String>)> = HashMap::new();
+) -> IndexMap<String, (String, Option<String>)> {
+ let mut map: IndexMap<String, (String, Option<String>)> = IndexMap::new();
let all_packages = lock
.packages
diff --git a/crates/mozart/src/commands/check_platform_reqs.rs b/crates/mozart/src/commands/check_platform_reqs.rs
index e9954f4..a890e0c 100644
--- a/crates/mozart/src/commands/check_platform_reqs.rs
+++ b/crates/mozart/src/commands/check_platform_reqs.rs
@@ -227,7 +227,7 @@ fn collect_from_installed_data(
no_dev: bool,
requirements: &mut BTreeMap<String, Vec<PlatformRequirement>>,
) {
- let dev_names: std::collections::HashSet<String> = installed
+ let dev_names: indexmap::IndexSet<String> = installed
.dev_package_names
.iter()
.map(|n| n.to_lowercase())
diff --git a/crates/mozart/src/commands/create_project.rs b/crates/mozart/src/commands/create_project.rs
index af77ba6..139550a 100644
--- a/crates/mozart/src/commands/create_project.rs
+++ b/crates/mozart/src/commands/create_project.rs
@@ -1,4 +1,5 @@
use clap::Args;
+use indexmap::IndexMap;
use mozart_core::console_format;
use mozart_core::package::{self, Stability};
use mozart_core::validation;
@@ -7,7 +8,6 @@ use mozart_registry::lockfile;
use mozart_registry::packagist;
use mozart_registry::resolver::{self, PlatformConfig, ResolveRequest};
use mozart_registry::version;
-use std::collections::HashMap;
use std::path::{Path, PathBuf};
#[derive(Args)]
@@ -413,7 +413,7 @@ pub async fn execute(
require_dev,
include_dev: dev_mode,
minimum_stability: proj_minimum_stability,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable: composer_prefer_stable,
prefer_lowest: false,
platform: PlatformConfig::new(),
@@ -422,7 +422,7 @@ pub async fn execute(
repositories: std::sync::Arc::new(
mozart_registry::repository::RepositorySet::with_packagist(repo_cache.clone()),
),
- temporary_constraints: HashMap::new(),
+ temporary_constraints: IndexMap::new(),
raw_repositories: raw.repositories.clone(),
root_provide: raw
.provide
diff --git a/crates/mozart/src/commands/dependency.rs b/crates/mozart/src/commands/dependency.rs
index 6dcaec8..d044432 100644
--- a/crates/mozart/src/commands/dependency.rs
+++ b/crates/mozart/src/commands/dependency.rs
@@ -4,7 +4,8 @@
//! `prohibits` (aka `why-not`) answers: "Which packages prevent version X of package Y from being
//! installed?"
-use std::collections::{BTreeMap, HashSet};
+use indexmap::IndexSet;
+use std::collections::BTreeMap;
use std::path::Path;
use anyhow::Result;
@@ -233,7 +234,7 @@ fn get_dependents_forward(
needles: &[String],
recursive: bool,
) -> Result<Vec<DependencyResult>> {
- let needle_set: HashSet<String> = needles.iter().map(|n| n.to_lowercase()).collect();
+ let needle_set: IndexSet<String> = needles.iter().map(|n| n.to_lowercase()).collect();
// Build name→PackageInfo lookup
let pkg_map: BTreeMap<String, &PackageInfo> = packages
@@ -243,7 +244,7 @@ fn get_dependents_forward(
if recursive {
// Recursive: BFS from needles upward to root, building a tree
- let mut visited: HashSet<String> = HashSet::new();
+ let mut visited: IndexSet<String> = IndexSet::new();
let mut results: Vec<DependencyResult> = Vec::new();
for needle in needles {
@@ -318,8 +319,8 @@ fn recurse_dependents(
packages: &[PackageInfo],
needle: &str,
pkg_map: &BTreeMap<String, &PackageInfo>,
- visited: &mut HashSet<String>,
- _original_needles: &HashSet<String>,
+ visited: &mut IndexSet<String>,
+ _original_needles: &IndexSet<String>,
) -> Vec<DependencyResult> {
let _ = pkg_map; // kept for potential future use
let direct = collect_direct_requires(packages, needle);
@@ -545,7 +546,7 @@ pub fn print_table(results: &[DependencyResult], console: &mozart_core::console:
.max()
.unwrap_or(0);
- let mut seen: HashSet<String> = HashSet::new();
+ let mut seen: IndexSet<String> = IndexSet::new();
for r in results {
let key = format!(
"{}|{}|{}|{}",
diff --git a/crates/mozart/src/commands/exec.rs b/crates/mozart/src/commands/exec.rs
index 350ff5c..eaaf465 100644
--- a/crates/mozart/src/commands/exec.rs
+++ b/crates/mozart/src/commands/exec.rs
@@ -171,8 +171,7 @@ fn get_binaries(working_dir: &Path, bin_dir: &Path) -> Vec<(String, bool)> {
// Collect from root composer.json bin entries
let composer_json_path = working_dir.join("composer.json");
if let Ok(root) = mozart_core::package::read_from_file(&composer_json_path) {
- let existing: std::collections::HashSet<&str> =
- binaries.iter().map(|(n, _)| n.as_str()).collect();
+ let existing: indexmap::IndexSet<&str> = binaries.iter().map(|(n, _)| n.as_str()).collect();
let mut local: Vec<String> = root
.bin
.iter()
diff --git a/crates/mozart/src/commands/install.rs b/crates/mozart/src/commands/install.rs
index 9555ba7..b38194e 100644
--- a/crates/mozart/src/commands/install.rs
+++ b/crates/mozart/src/commands/install.rs
@@ -1,4 +1,5 @@
use clap::Args;
+use indexmap::IndexSet;
use mozart_core::console;
use mozart_core::console_format;
use mozart_registry::installed;
@@ -6,7 +7,7 @@ use mozart_registry::installer_executor::{
ExecuteContext, FilesystemExecutor, InstallerExecutor, PackageOperation,
};
use mozart_registry::lockfile;
-use std::collections::{BTreeMap, HashSet};
+use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
#[derive(Args)]
@@ -198,7 +199,7 @@ pub fn compute_operations<'a>(
}
// Compute removals: packages in installed but not in locked
- let locked_names: HashSet<String> = locked.iter().map(|p| p.name.to_lowercase()).collect();
+ let locked_names: IndexSet<String> = locked.iter().map(|p| p.name.to_lowercase()).collect();
let removals: Vec<String> = installed
.packages
@@ -254,7 +255,7 @@ fn topological_sort<'a>(
// Identify root packages: those not pulled in by any other package's
// requires (counting provides/replaces as a match).
- let mut required_by_others: HashSet<String> = HashSet::new();
+ let mut required_by_others: IndexSet<String> = IndexSet::new();
for pkg in &sorted {
let pkg_lower = pkg.name.to_lowercase();
for dep in pkg.require.keys() {
@@ -276,8 +277,8 @@ fn topological_sort<'a>(
.copied()
.collect();
- let mut visited: HashSet<String> = HashSet::new();
- let mut processed: HashSet<String> = HashSet::new();
+ let mut visited: IndexSet<String> = IndexSet::new();
+ let mut processed: IndexSet<String> = IndexSet::new();
let mut ordered: Vec<&'a lockfile::LockedPackage> = Vec::with_capacity(packages.len());
while let Some(pkg) = stack.pop() {
@@ -444,7 +445,7 @@ fn check_platform_requirements_against(
return Vec::new();
}
- let ignored: HashSet<String> = ignore_platform_req
+ let ignored: IndexSet<String> = ignore_platform_req
.iter()
.map(|s| s.to_lowercase())
.collect();
@@ -503,7 +504,7 @@ fn warn_platform_requirements(
return;
}
- let ignored_set: HashSet<String> = ignore_platform_req
+ let ignored_set: IndexSet<String> = ignore_platform_req
.iter()
.map(|s| s.to_lowercase())
.collect();
diff --git a/crates/mozart/src/commands/licenses.rs b/crates/mozart/src/commands/licenses.rs
index b066fde..5ce2b35 100644
--- a/crates/mozart/src/commands/licenses.rs
+++ b/crates/mozart/src/commands/licenses.rs
@@ -1,7 +1,7 @@
use clap::Args;
+use indexmap::IndexSet;
use mozart_core::console::{Console, Verbosity};
use serde::Serialize;
-use std::collections::HashSet;
use std::path::{Path, PathBuf};
#[derive(Args)]
@@ -102,7 +102,7 @@ fn load_installed_licenses(working_dir: &Path, no_dev: bool) -> anyhow::Result<V
let vendor_dir = working_dir.join("vendor");
let installed = mozart_registry::installed::InstalledPackages::read(&vendor_dir)?;
- let dev_names: HashSet<String> = installed
+ let dev_names: IndexSet<String> = installed
.dev_package_names
.iter()
.map(|n| n.to_lowercase())
diff --git a/crates/mozart/src/commands/outdated.rs b/crates/mozart/src/commands/outdated.rs
index 4d0226d..5a4f854 100644
--- a/crates/mozart/src/commands/outdated.rs
+++ b/crates/mozart/src/commands/outdated.rs
@@ -1,7 +1,7 @@
use clap::Args;
+use indexmap::IndexSet;
use mozart_core::matches_wildcard;
use std::cmp::Ordering;
-use std::collections::HashSet;
use std::path::{Path, PathBuf};
#[derive(Args)]
@@ -136,14 +136,14 @@ pub async fn execute(
};
// Build set of direct dependency names
- let direct_names: HashSet<String> = if let Some(ref root) = root_package {
- let mut names: HashSet<String> = root.require.keys().map(|k| k.to_lowercase()).collect();
+ let direct_names: IndexSet<String> = if let Some(ref root) = root_package {
+ let mut names: IndexSet<String> = root.require.keys().map(|k| k.to_lowercase()).collect();
if !args.no_dev {
names.extend(root.require_dev.keys().map(|k| k.to_lowercase()));
}
names
} else {
- HashSet::new()
+ IndexSet::new()
};
// Process each package
@@ -242,7 +242,7 @@ fn load_installed_packages(working_dir: &Path, no_dev: bool) -> anyhow::Result<V
let vendor_dir = working_dir.join("vendor");
let installed = mozart_registry::installed::InstalledPackages::read(&vendor_dir)?;
- let dev_names: HashSet<String> = installed
+ let dev_names: IndexSet<String> = installed
.dev_package_names
.iter()
.map(|n| n.to_lowercase())
diff --git a/crates/mozart/src/commands/reinstall.rs b/crates/mozart/src/commands/reinstall.rs
index c19d926..2df55c5 100644
--- a/crates/mozart/src/commands/reinstall.rs
+++ b/crates/mozart/src/commands/reinstall.rs
@@ -112,7 +112,7 @@ pub async fn execute(
// Step 5: Determine packages to reinstall.
// Build the full set of installed packages (prod + dev unless --no-dev).
- let dev_package_names: std::collections::HashSet<String> = installed
+ let dev_package_names: indexmap::IndexSet<String> = installed
.dev_package_names
.iter()
.map(|n| n.to_lowercase())
@@ -666,7 +666,7 @@ mod tests {
installed.packages.push(e2.clone());
installed.dev_package_names = vec!["phpunit/phpunit".to_string()];
- let dev_package_names: std::collections::HashSet<String> = installed
+ let dev_package_names: indexmap::IndexSet<String> = installed
.dev_package_names
.iter()
.map(|n| n.to_lowercase())
diff --git a/crates/mozart/src/commands/remove.rs b/crates/mozart/src/commands/remove.rs
index 20cb6a2..df8bf2b 100644
--- a/crates/mozart/src/commands/remove.rs
+++ b/crates/mozart/src/commands/remove.rs
@@ -1,11 +1,11 @@
use clap::Args;
+use indexmap::IndexMap;
use mozart_core::console::Verbosity;
use mozart_core::console_format;
use mozart_core::package;
use mozart_core::validation;
use mozart_registry::lockfile;
use mozart_registry::resolver::{self, PlatformConfig, ResolveRequest};
-use std::collections::HashMap;
#[derive(Args)]
pub struct RemoveArgs {
@@ -247,7 +247,7 @@ pub async fn execute(
require_dev,
include_dev: dev_mode,
minimum_stability,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable: composer_prefer_stable,
prefer_lowest: false,
platform: PlatformConfig::new(),
@@ -256,7 +256,7 @@ pub async fn execute(
repositories: std::sync::Arc::new(
mozart_registry::repository::RepositorySet::with_packagist(repo_cache.clone()),
),
- temporary_constraints: HashMap::new(),
+ temporary_constraints: IndexMap::new(),
raw_repositories: raw.repositories.clone(),
root_provide: raw
.provide
@@ -517,7 +517,7 @@ async fn remove_unused(
require_dev,
include_dev: dev_mode,
minimum_stability,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable: composer_prefer_stable,
prefer_lowest: false,
platform: PlatformConfig::new(),
@@ -526,7 +526,7 @@ async fn remove_unused(
repositories: std::sync::Arc::new(
mozart_registry::repository::RepositorySet::with_packagist(repo_cache.clone()),
),
- temporary_constraints: HashMap::new(),
+ temporary_constraints: IndexMap::new(),
raw_repositories: raw.repositories.clone(),
root_provide: raw
.provide
@@ -550,7 +550,7 @@ async fn remove_unused(
})?;
// Build set of resolved package names
- let resolved_names: std::collections::HashSet<String> =
+ let resolved_names: indexmap::IndexSet<String> =
resolved.iter().map(|p| p.name.to_lowercase()).collect();
// Find packages in the old lock that are not in the new resolution
@@ -847,9 +847,9 @@ mod tests {
#[tokio::test]
#[ignore]
async fn test_remove_full_e2e() {
+ use indexmap::IndexMap;
use mozart_registry::lockfile::{LockFileGenerationRequest, generate_lock_file};
use mozart_registry::resolver::{ResolveRequest, resolve};
- use std::collections::HashMap;
use tempfile::tempdir;
let dir = tempdir().unwrap();
@@ -870,7 +870,7 @@ mod tests {
require_dev: vec![],
include_dev: false,
minimum_stability: mozart_core::package::Stability::Stable,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable: true,
prefer_lowest: false,
platform: mozart_registry::resolver::PlatformConfig::new(),
@@ -884,10 +884,10 @@ mod tests {
),
),
),
- temporary_constraints: HashMap::new(),
+ temporary_constraints: IndexMap::new(),
raw_repositories: vec![],
- root_provide: HashMap::new(),
- root_replace: HashMap::new(),
+ root_provide: IndexMap::new(),
+ root_replace: IndexMap::new(),
};
let resolved = resolve(&request)
.await
@@ -923,7 +923,7 @@ mod tests {
require_dev: vec![],
include_dev: false,
minimum_stability: mozart_core::package::Stability::Stable,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable: true,
prefer_lowest: false,
platform: mozart_registry::resolver::PlatformConfig::new(),
@@ -937,10 +937,10 @@ mod tests {
),
),
),
- temporary_constraints: HashMap::new(),
+ temporary_constraints: IndexMap::new(),
raw_repositories: vec![],
- root_provide: HashMap::new(),
- root_replace: HashMap::new(),
+ root_provide: IndexMap::new(),
+ root_replace: IndexMap::new(),
};
let resolved2 = resolve(&request2)
.await
diff --git a/crates/mozart/src/commands/repository.rs b/crates/mozart/src/commands/repository.rs
index 0931b66..e8ca920 100644
--- a/crates/mozart/src/commands/repository.rs
+++ b/crates/mozart/src/commands/repository.rs
@@ -1085,7 +1085,7 @@ mod tests {
});
let result = normalize_repositories(&val);
assert_eq!(result.len(), 2);
- let names: std::collections::HashSet<&str> = result
+ let names: indexmap::IndexSet<&str> = result
.iter()
.filter_map(|v| v.get("name").and_then(|n| n.as_str()))
.collect();
diff --git a/crates/mozart/src/commands/require.rs b/crates/mozart/src/commands/require.rs
index 95b26ea..69d7ea2 100644
--- a/crates/mozart/src/commands/require.rs
+++ b/crates/mozart/src/commands/require.rs
@@ -1,4 +1,5 @@
use clap::Args;
+use indexmap::IndexMap;
use mozart_core::console::Verbosity;
use mozart_core::console_format;
use mozart_core::package::{self, Stability};
@@ -7,7 +8,6 @@ use mozart_registry::lockfile;
use mozart_registry::packagist;
use mozart_registry::resolver::{self, PlatformConfig, ResolveRequest};
use mozart_registry::version;
-use std::collections::HashMap;
use std::io::{BufRead, IsTerminal, Write};
#[derive(Args)]
@@ -133,7 +133,7 @@ pub struct RequireArgs {
/// Returns a list of `"vendor/package:constraint"` strings that the user confirmed,
/// or an empty vec if the user typed nothing / pressed Ctrl-D immediately.
async fn interactive_search_packages(
- already_required: &std::collections::HashSet<String>,
+ already_required: &indexmap::IndexSet<String>,
preferred_stability: Stability,
fixed: bool,
repo_cache: &mozart_registry::cache::Cache,
@@ -359,8 +359,7 @@ pub async fn execute(
let raw_check = package::read_from_file(&composer_path)?;
// Build set of already-required packages
- let mut already_required: std::collections::HashSet<String> =
- std::collections::HashSet::new();
+ let mut already_required: indexmap::IndexSet<String> = indexmap::IndexSet::new();
for k in raw_check.require.keys() {
already_required.insert(k.to_lowercase());
}
@@ -636,7 +635,7 @@ pub async fn execute(
require_dev,
include_dev: dev_mode,
minimum_stability,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable,
prefer_lowest: args.prefer_lowest,
platform: PlatformConfig::new(),
@@ -645,7 +644,7 @@ pub async fn execute(
repositories: std::sync::Arc::new(
mozart_registry::repository::RepositorySet::with_packagist(repo_cache.clone()),
),
- temporary_constraints: HashMap::new(),
+ temporary_constraints: IndexMap::new(),
raw_repositories: raw.repositories.clone(),
root_provide: raw
.provide
@@ -1036,7 +1035,7 @@ mod tests {
require_dev: vec![],
include_dev: false,
minimum_stability: Stability::Stable,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable: true,
prefer_lowest: false,
platform: PlatformConfig::new(),
@@ -1050,10 +1049,10 @@ mod tests {
),
),
),
- temporary_constraints: HashMap::new(),
+ temporary_constraints: IndexMap::new(),
raw_repositories: vec![],
- root_provide: HashMap::new(),
- root_replace: HashMap::new(),
+ root_provide: IndexMap::new(),
+ root_replace: IndexMap::new(),
};
let resolved = resolver::resolve(&request)
@@ -1106,7 +1105,7 @@ mod tests {
require_dev: vec![],
include_dev: false,
minimum_stability: Stability::Stable,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable: true,
prefer_lowest: false,
platform: PlatformConfig::new(),
@@ -1120,10 +1119,10 @@ mod tests {
),
),
),
- temporary_constraints: HashMap::new(),
+ temporary_constraints: IndexMap::new(),
raw_repositories: vec![],
- root_provide: HashMap::new(),
- root_replace: HashMap::new(),
+ root_provide: IndexMap::new(),
+ root_replace: IndexMap::new(),
};
let resolved = resolver::resolve(&request)
diff --git a/crates/mozart/src/commands/search.rs b/crates/mozart/src/commands/search.rs
index accd6af..023bfdf 100644
--- a/crates/mozart/src/commands/search.rs
+++ b/crates/mozart/src/commands/search.rs
@@ -139,7 +139,7 @@ pub async fn execute(
// Deduplicate to unique vendor names (Composer returns vendor-only names
// for SEARCH_VENDOR mode).
- let mut seen = std::collections::HashSet::new();
+ let mut seen = indexmap::IndexSet::new();
let mut vendor_names: Vec<String> = Vec::new();
for r in &results {
let vendor = r.name.split('/').next().unwrap_or("").to_string();
@@ -515,7 +515,7 @@ mod tests {
];
let refs: Vec<&SearchResult> = results.iter().collect();
- let mut seen = std::collections::HashSet::new();
+ let mut seen = indexmap::IndexSet::new();
let mut vendor_names: Vec<String> = Vec::new();
for r in &refs {
let vendor = r.name.split('/').next().unwrap_or("").to_string();
diff --git a/crates/mozart/src/commands/show.rs b/crates/mozart/src/commands/show.rs
index fa77321..c59a595 100644
--- a/crates/mozart/src/commands/show.rs
+++ b/crates/mozart/src/commands/show.rs
@@ -1,8 +1,8 @@
use clap::Args;
+use indexmap::{IndexMap, IndexSet};
use mozart_core::console::Verbosity;
use mozart_core::console_format;
use mozart_core::matches_wildcard;
-use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
#[derive(Args)]
@@ -292,7 +292,7 @@ fn filter_installed_packages<'a>(
// --no-dev: exclude dev packages
if args.no_dev {
- let dev_names: HashSet<String> = installed
+ let dev_names: IndexSet<String> = installed
.dev_package_names
.iter()
.map(|n| n.to_lowercase())
@@ -305,7 +305,7 @@ fn filter_installed_packages<'a>(
let composer_json_path = working_dir.join("composer.json");
if composer_json_path.exists() {
let root = mozart_core::package::read_from_file(&composer_json_path)?;
- let mut direct_names: HashSet<String> =
+ let mut direct_names: IndexSet<String> =
root.require.keys().map(|k| k.to_lowercase()).collect();
if !args.no_dev {
direct_names.extend(root.require_dev.keys().map(|k| k.to_lowercase()));
@@ -812,7 +812,7 @@ async fn execute_locked(
let composer_json_path = working_dir.join("composer.json");
if composer_json_path.exists() {
let root = mozart_core::package::read_from_file(&composer_json_path)?;
- let mut direct_names: HashSet<String> =
+ let mut direct_names: IndexSet<String> =
root.require.keys().map(|k| k.to_lowercase()).collect();
if !args.no_dev {
direct_names.extend(root.require_dev.keys().map(|k| k.to_lowercase()));
@@ -1346,7 +1346,7 @@ fn show_tree(
let root = mozart_core::package::read_from_file(&composer_json_path)?;
// Load all locked packages into a map for quick lookup
- let pkg_map: HashMap<String, &mozart_registry::lockfile::LockedPackage>;
+ let pkg_map: IndexMap<String, &mozart_registry::lockfile::LockedPackage>;
let lock_storage;
if lock_path.exists() {
lock_storage = mozart_registry::lockfile::LockFile::read_from_file(&lock_path)?;
@@ -1357,7 +1357,7 @@ fn show_tree(
.map(|p| (p.name.to_lowercase(), p))
.collect();
} else {
- pkg_map = HashMap::new();
+ pkg_map = IndexMap::new();
}
// Determine roots to display: package filter or full tree
@@ -1389,7 +1389,7 @@ fn show_tree(
);
// Render each root dependency as a tree
- let mut visited_global: HashSet<String> = HashSet::new();
+ let mut visited_global: IndexSet<String> = IndexSet::new();
let count = root_reqs.len();
for (i, (dep_name, dep_constraint)) in root_reqs.iter().enumerate() {
let is_last = i == count - 1;
@@ -1415,10 +1415,10 @@ fn show_tree(
fn print_tree_node(
pkg_name: &str,
constraint: &str,
- pkg_map: &HashMap<String, &mozart_registry::lockfile::LockedPackage>,
+ pkg_map: &IndexMap<String, &mozart_registry::lockfile::LockedPackage>,
prefix: &str,
child_prefix: &str,
- visited: &mut HashSet<String>,
+ visited: &mut IndexSet<String>,
depth: usize,
console: &mozart_core::console::Console,
) {
@@ -1491,7 +1491,7 @@ fn print_tree_node(
);
}
- visited.remove(&key);
+ visited.shift_remove(&key);
} else {
// Package not found in lock file (platform package or not installed)
if !is_platform_package(&key) {
diff --git a/crates/mozart/src/commands/status.rs b/crates/mozart/src/commands/status.rs
index 6d8fc98..29b1e1b 100644
--- a/crates/mozart/src/commands/status.rs
+++ b/crates/mozart/src/commands/status.rs
@@ -1,7 +1,7 @@
use clap::Args;
+use indexmap::IndexMap;
use mozart_core::console::Verbosity;
use sha1::{Digest, Sha1};
-use std::collections::HashMap;
use std::path::{Path, PathBuf};
#[derive(Args)]
@@ -293,8 +293,8 @@ fn make_temp_dir(package_name: &str) -> anyhow::Result<PathBuf> {
/// Recursively hash all files in a directory.
///
/// Returns a map from relative path string to SHA-1 hex digest.
-fn hash_directory(dir: &Path) -> anyhow::Result<HashMap<String, String>> {
- let mut map = HashMap::new();
+fn hash_directory(dir: &Path) -> anyhow::Result<IndexMap<String, String>> {
+ let mut map = IndexMap::new();
hash_dir_recursive(dir, dir, &mut map)?;
Ok(map)
}
@@ -302,7 +302,7 @@ fn hash_directory(dir: &Path) -> anyhow::Result<HashMap<String, String>> {
fn hash_dir_recursive(
root: &Path,
current: &Path,
- map: &mut HashMap<String, String>,
+ map: &mut IndexMap<String, String>,
) -> anyhow::Result<()> {
let entries = match std::fs::read_dir(current) {
Ok(e) => e,
@@ -337,8 +337,8 @@ fn hash_dir_recursive(
/// Compare two hash maps (original vs installed) and return a list of changes.
fn compute_diff(
- original: &HashMap<String, String>,
- installed: &HashMap<String, String>,
+ original: &IndexMap<String, String>,
+ installed: &IndexMap<String, String>,
) -> Vec<FileChange> {
let mut changes: Vec<FileChange> = Vec::new();
@@ -418,7 +418,7 @@ mod tests {
#[test]
fn test_compute_diff_no_changes() {
- let mut map: HashMap<String, String> = HashMap::new();
+ let mut map: IndexMap<String, String> = IndexMap::new();
map.insert("src/Foo.php".to_string(), "abc123".to_string());
map.insert("src/Bar.php".to_string(), "def456".to_string());
@@ -430,10 +430,10 @@ mod tests {
#[test]
fn test_compute_diff_modified() {
- let mut original: HashMap<String, String> = HashMap::new();
+ let mut original: IndexMap<String, String> = IndexMap::new();
original.insert("src/Foo.php".to_string(), "abc123".to_string());
- let mut installed: HashMap<String, String> = HashMap::new();
+ let mut installed: IndexMap<String, String> = IndexMap::new();
installed.insert("src/Foo.php".to_string(), "xyz999".to_string());
let changes = compute_diff(&original, &installed);
@@ -446,9 +446,9 @@ mod tests {
#[test]
fn test_compute_diff_added() {
- let original: HashMap<String, String> = HashMap::new();
+ let original: IndexMap<String, String> = IndexMap::new();
- let mut installed: HashMap<String, String> = HashMap::new();
+ let mut installed: IndexMap<String, String> = IndexMap::new();
installed.insert("src/NewFile.php".to_string(), "aabbcc".to_string());
let changes = compute_diff(&original, &installed);
@@ -461,10 +461,10 @@ mod tests {
#[test]
fn test_compute_diff_removed() {
- let mut original: HashMap<String, String> = HashMap::new();
+ let mut original: IndexMap<String, String> = IndexMap::new();
original.insert("src/OldFile.php".to_string(), "112233".to_string());
- let installed: HashMap<String, String> = HashMap::new();
+ let installed: IndexMap<String, String> = IndexMap::new();
let changes = compute_diff(&original, &installed);
assert_eq!(changes.len(), 1);
@@ -476,12 +476,12 @@ mod tests {
#[test]
fn test_compute_diff_mixed() {
- let mut original: HashMap<String, String> = HashMap::new();
+ let mut original: IndexMap<String, String> = IndexMap::new();
original.insert("src/Unchanged.php".to_string(), "same".to_string());
original.insert("src/Modified.php".to_string(), "old".to_string());
original.insert("src/Removed.php".to_string(), "gone".to_string());
- let mut installed: HashMap<String, String> = HashMap::new();
+ let mut installed: IndexMap<String, String> = IndexMap::new();
installed.insert("src/Unchanged.php".to_string(), "same".to_string());
installed.insert("src/Modified.php".to_string(), "new".to_string());
installed.insert("src/Added.php".to_string(), "extra".to_string());
diff --git a/crates/mozart/src/commands/suggests.rs b/crates/mozart/src/commands/suggests.rs
index 394778f..6d1765e 100644
--- a/crates/mozart/src/commands/suggests.rs
+++ b/crates/mozart/src/commands/suggests.rs
@@ -1,8 +1,10 @@
use clap::Args;
+use indexmap::IndexMap;
+use indexmap::IndexSet;
use mozart_core::console;
use mozart_core::console::Verbosity;
use mozart_core::console_format;
-use std::collections::{BTreeMap, HashMap, HashSet};
+use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
#[derive(Args)]
@@ -76,17 +78,17 @@ pub async fn execute(
};
// 3. Determine direct-deps-only filter
- let (package_filter, direct_deps_only): (HashSet<String>, Option<HashSet<String>>) = {
+ let (package_filter, direct_deps_only): (IndexSet<String>, Option<IndexSet<String>>) = {
if !args.packages.is_empty() {
// Filter by the explicitly named packages
- let filter: HashSet<String> = args.packages.iter().map(|s| s.to_lowercase()).collect();
+ let filter: IndexSet<String> = args.packages.iter().map(|s| s.to_lowercase()).collect();
(filter, None)
} else if args.all {
- (HashSet::new(), None)
+ (IndexSet::new(), None)
} else {
// Default: only direct deps from composer.json
let direct = compute_direct_deps(&working_dir)?;
- (HashSet::new(), Some(direct))
+ (IndexSet::new(), Some(direct))
}
};
@@ -210,7 +212,7 @@ fn collect_suggestions_from_installed(
}
}
- let dev_names: HashSet<String> = installed
+ let dev_names: IndexSet<String> = installed
.dev_package_names
.iter()
.map(|n| n.to_lowercase())
@@ -273,11 +275,11 @@ fn collect_suggestions_from_root(working_dir: &Path) -> anyhow::Result<Vec<Sugge
fn collect_installed_names_from_lock(
working_dir: &Path,
no_dev: bool,
-) -> anyhow::Result<HashSet<String>> {
+) -> anyhow::Result<IndexSet<String>> {
let lock_path = working_dir.join("composer.lock");
let lock = mozart_registry::lockfile::LockFile::read_from_file(&lock_path)?;
- let mut names: HashSet<String> = HashSet::new();
+ let mut names: IndexSet<String> = IndexSet::new();
let mut all_packages: Vec<&mozart_registry::lockfile::LockedPackage> =
lock.packages.iter().collect();
@@ -309,17 +311,17 @@ fn collect_installed_names_from_lock(
fn collect_installed_names_from_installed(
working_dir: &Path,
no_dev: bool,
-) -> anyhow::Result<HashSet<String>> {
+) -> anyhow::Result<IndexSet<String>> {
let vendor_dir = working_dir.join("vendor");
let installed = mozart_registry::installed::InstalledPackages::read(&vendor_dir)?;
- let dev_names: HashSet<String> = installed
+ let dev_names: IndexSet<String> = installed
.dev_package_names
.iter()
.map(|n| n.to_lowercase())
.collect();
- let mut names: HashSet<String> = HashSet::new();
+ let mut names: IndexSet<String> = IndexSet::new();
for pkg in &installed.packages {
if no_dev && dev_names.contains(&pkg.name.to_lowercase()) {
@@ -356,7 +358,7 @@ fn collect_installed_names_from_installed(
fn add_platform_names_from_lock(
lock: &mozart_registry::lockfile::LockFile,
- names: &mut HashSet<String>,
+ names: &mut IndexSet<String>,
) {
// Collect platform keys from the lock's platform and platform_dev objects
if let Some(obj) = lock.platform.as_object() {
@@ -382,13 +384,13 @@ fn is_platform_package(name: &str) -> bool {
// ─── Direct deps helper ───────────────────────────────────────────────────────
-fn compute_direct_deps(working_dir: &Path) -> anyhow::Result<HashSet<String>> {
+fn compute_direct_deps(working_dir: &Path) -> anyhow::Result<IndexSet<String>> {
let composer_json_path = working_dir.join("composer.json");
if !composer_json_path.exists() {
- return Ok(HashSet::new());
+ return Ok(IndexSet::new());
}
let root = mozart_core::package::read_from_file(&composer_json_path)?;
- let mut deps: HashSet<String> = HashSet::new();
+ let mut deps: IndexSet<String> = IndexSet::new();
// Include the root package itself so its suggestions are shown
if !root.name.is_empty() {
deps.insert(root.name.to_lowercase());
@@ -417,7 +419,7 @@ fn sanitize_reason(reason: &str) -> String {
/// If the same source suggests the same target multiple times, the last reason wins.
/// This matches Composer's behavior where map insertion overwrites previous entries.
fn deduplicate_suggestions(suggestions: Vec<Suggestion>) -> Vec<Suggestion> {
- let mut seen: HashMap<(String, String), usize> = HashMap::new();
+ let mut seen: IndexMap<(String, String), usize> = IndexMap::new();
let mut deduped: Vec<Suggestion> = Vec::new();
for s in suggestions {
@@ -648,7 +650,7 @@ mod tests {
];
let refs: Vec<&Suggestion> = suggestions.iter().collect();
- let mut installed: HashSet<String> = HashSet::new();
+ let mut installed: IndexSet<String> = IndexSet::new();
installed.insert("ext-intl".to_string());
installed.insert("ext-mbstring".to_string());
@@ -671,7 +673,7 @@ mod tests {
];
let refs: Vec<&Suggestion> = suggestions.iter().collect();
- let mut filter: HashSet<String> = HashSet::new();
+ let mut filter: IndexSet<String> = IndexSet::new();
filter.insert("vendor/a".to_string());
filter.insert("vendor/c".to_string());
@@ -694,7 +696,7 @@ mod tests {
];
let refs: Vec<&Suggestion> = suggestions.iter().collect();
- let mut direct: HashSet<String> = HashSet::new();
+ let mut direct: IndexSet<String> = IndexSet::new();
direct.insert("vendor/direct".to_string());
let filtered: Vec<&Suggestion> = refs
@@ -715,7 +717,7 @@ mod tests {
make_suggestion("vendor/c", "vendor/z", ""),
];
let refs: Vec<&Suggestion> = suggestions.iter().collect();
- let installed: HashSet<String> = HashSet::new();
+ let installed: IndexSet<String> = IndexSet::new();
let filtered: Vec<&Suggestion> = refs
.iter()
@@ -755,7 +757,7 @@ mod tests {
let suggestions = collect_suggestions_from_locked(working_dir, false).unwrap();
assert_eq!(suggestions.len(), 2);
assert!(suggestions.iter().all(|s| s.source == "vendor/a"));
- let targets: HashSet<&str> = suggestions.iter().map(|s| s.target.as_str()).collect();
+ let targets: IndexSet<&str> = suggestions.iter().map(|s| s.target.as_str()).collect();
assert!(targets.contains("ext-intl"));
assert!(targets.contains("vendor/optional"));
}
diff --git a/crates/mozart/src/commands/update.rs b/crates/mozart/src/commands/update.rs
index 17b7c97..0c25a9e 100644
--- a/crates/mozart/src/commands/update.rs
+++ b/crates/mozart/src/commands/update.rs
@@ -1,10 +1,10 @@
use clap::Args;
+use indexmap::{IndexMap, IndexSet};
use mozart_core::console;
use mozart_core::console_format;
use mozart_core::package::{self, Stability};
use mozart_registry::lockfile;
use mozart_registry::resolver::{self, PlatformConfig, ResolveRequest, ResolvedPackage};
-use std::collections::{HashMap, HashSet};
#[derive(Args)]
pub struct UpdateArgs {
@@ -198,7 +198,7 @@ pub fn compute_update_changes(
dev_mode: bool,
) -> Vec<UpdateChange> {
// Build map of old lock packages keyed by lowercase name -> version string
- let mut old_map: HashMap<String, String> = HashMap::new();
+ let mut old_map: IndexMap<String, String> = IndexMap::new();
if let Some(old) = old_lock {
for pkg in &old.packages {
old_map.insert(pkg.name.to_lowercase(), pkg.version.clone());
@@ -211,7 +211,7 @@ pub fn compute_update_changes(
}
// Build map of new lock packages keyed by lowercase name -> version string
- let mut new_map: HashMap<String, String> = HashMap::new();
+ let mut new_map: IndexMap<String, String> = IndexMap::new();
for pkg in &new_lock.packages {
new_map.insert(pkg.name.to_lowercase(), pkg.version.clone());
}
@@ -302,11 +302,11 @@ pub fn apply_partial_update(
update_packages: &[String],
) -> Vec<ResolvedPackage> {
// Build a set of normalized package names we want to update
- let update_set: std::collections::HashSet<String> =
+ let update_set: indexmap::IndexSet<String> =
update_packages.iter().map(|s| s.to_lowercase()).collect();
// Build a map of old locked packages by name -> (version, version_normalized, is_dev)
- let mut old_pkg_map: HashMap<String, &lockfile::LockedPackage> = HashMap::new();
+ let mut old_pkg_map: IndexMap<String, &lockfile::LockedPackage> = IndexMap::new();
for pkg in &old_lock.packages {
old_pkg_map.insert(pkg.name.to_lowercase(), pkg);
}
@@ -414,7 +414,7 @@ pub fn expand_wildcards(
.collect();
let mut result: Vec<String> = Vec::new();
- let mut seen: HashSet<String> = HashSet::new();
+ let mut seen: IndexSet<String> = IndexSet::new();
for spec in specifiers {
if spec.contains('*') {
@@ -448,8 +448,8 @@ pub fn expand_wildcards(
// ─────────────────────────────────────────────────────────────────────────────
/// Build a lookup map from package name (lowercase) to its LockedPackage.
-fn build_lock_map(lock: &lockfile::LockFile) -> HashMap<String, &lockfile::LockedPackage> {
- let mut map = HashMap::new();
+fn build_lock_map(lock: &lockfile::LockFile) -> IndexMap<String, &lockfile::LockedPackage> {
+ let mut map = IndexMap::new();
for pkg in &lock.packages {
map.insert(pkg.name.to_lowercase(), pkg);
}
@@ -468,7 +468,7 @@ pub fn expand_with_direct_dependencies(
lock: &lockfile::LockFile,
) -> Vec<String> {
let lock_map = build_lock_map(lock);
- let mut result_set: HashSet<String> = packages.iter().cloned().collect();
+ let mut result_set: IndexSet<String> = packages.iter().cloned().collect();
let mut result: Vec<String> = packages;
for name in result.clone() {
@@ -503,7 +503,7 @@ pub fn expand_with_all_dependencies(
lock: &lockfile::LockFile,
) -> Vec<String> {
let lock_map = build_lock_map(lock);
- let mut result_set: HashSet<String> = packages.iter().cloned().collect();
+ let mut result_set: IndexSet<String> = packages.iter().cloned().collect();
let mut queue: Vec<String> = packages.clone();
let mut result: Vec<String> = packages;
@@ -665,7 +665,7 @@ pub fn apply_patch_only(
resolved: Vec<ResolvedPackage>,
old_lock: &lockfile::LockFile,
) -> Vec<ResolvedPackage> {
- let mut old_pkg_map: HashMap<String, &lockfile::LockedPackage> = HashMap::new();
+ let mut old_pkg_map: IndexMap<String, &lockfile::LockedPackage> = IndexMap::new();
for pkg in &old_lock.packages {
old_pkg_map.insert(pkg.name.to_lowercase(), pkg);
}
@@ -806,7 +806,7 @@ pub async fn run(
let dev_mode = !args.no_dev;
// Fix 1C + Fix 2: Parse --with constraints and inline constraint shorthand.
- let mut temporary_constraints: HashMap<String, String> = HashMap::new();
+ let mut temporary_constraints: IndexMap<String, String> = IndexMap::new();
// Parse --with constraints (format: "vendor/package:constraint")
for with_entry in &args.with {
@@ -887,7 +887,7 @@ pub async fn run(
require_dev,
include_dev: dev_mode,
minimum_stability,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable,
prefer_lowest: args.prefer_lowest,
platform,
@@ -1166,7 +1166,7 @@ pub async fn run(
let bump_require_dev = mode == "all" || mode == "dev";
// Build locked versions map from the new lock
- let mut locked_versions: HashMap<String, (String, Option<String>)> = HashMap::new();
+ let mut locked_versions: IndexMap<String, (String, Option<String>)> = IndexMap::new();
for pkg in &new_lock.packages {
locked_versions.insert(
pkg.name.to_lowercase(),
@@ -1997,7 +1997,7 @@ mod tests {
require_dev: vec![],
include_dev: false,
minimum_stability: Stability::Stable,
- stability_flags: HashMap::new(),
+ stability_flags: IndexMap::new(),
prefer_stable: true,
prefer_lowest: false,
platform: PlatformConfig::new(),
@@ -2011,10 +2011,10 @@ mod tests {
),
),
),
- temporary_constraints: HashMap::new(),
+ temporary_constraints: IndexMap::new(),
raw_repositories: vec![],
- root_provide: HashMap::new(),
- root_replace: HashMap::new(),
+ root_provide: IndexMap::new(),
+ root_replace: IndexMap::new(),
};
let resolved = resolve(&request).await.expect("Resolution should succeed");
diff --git a/crates/mozart/src/commands/validate.rs b/crates/mozart/src/commands/validate.rs
index cec36b5..e2345c8 100644
--- a/crates/mozart/src/commands/validate.rs
+++ b/crates/mozart/src/commands/validate.rs
@@ -421,7 +421,7 @@ fn check_scripts_orphans(
obj: &serde_json::Map<String, serde_json::Value>,
result: &mut ValidationResult,
) {
- let script_keys: std::collections::HashSet<&str> = obj
+ let script_keys: indexmap::IndexSet<&str> = obj
.get("scripts")
.and_then(|v| v.as_object())
.map(|m| m.keys().map(|k| k.as_str()).collect())