From ae1aa6540761e54a76b8f7984cf93cd3a0d011d0 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 3 May 2026 11:55:03 +0900 Subject: refactor: switch internal maps/sets from HashMap to IndexMap Adopt indexmap workspace-wide so iteration order is deterministic and follows insertion order. The non-deterministic order of std HashMap otherwise leaks into resolver decisions when multiple valid solutions exist (e.g. cyclic require pairs under prefer-lowest), making behavior flaky and divergent from Composer's PHP-array semantics. Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/mozart/src/commands/show.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'crates/mozart/src/commands/show.rs') 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 = installed + let dev_names: IndexSet = 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 = + let mut direct_names: IndexSet = 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 = + let mut direct_names: IndexSet = 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; + let pkg_map: IndexMap; 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 = HashSet::new(); + let mut visited_global: IndexSet = 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, + pkg_map: &IndexMap, prefix: &str, child_prefix: &str, - visited: &mut HashSet, + visited: &mut IndexSet, 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) { -- cgit v1.3.1