aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart/src/commands/status.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-09 19:59:58 +0900
committernsfisis <nsfisis@gmail.com>2026-05-09 19:59:58 +0900
commit72b2e877c01e67ba7edd37e34ac2eadb7a1c62c4 (patch)
treeddccef3355d76f759b3cf43af0fcc3c8b79eaa6d /crates/mozart/src/commands/status.rs
parentf9671f2dcde92d5c037595d0d3f01396a8190970 (diff)
downloadphp-mozart-72b2e877c01e67ba7edd37e34ac2eadb7a1c62c4.tar.gz
php-mozart-72b2e877c01e67ba7edd37e34ac2eadb7a1c62c4.tar.zst
php-mozart-72b2e877c01e67ba7edd37e34ac2eadb7a1c62c4.zip
refactor(vcs): mirror Composer interfaces; rename get_local_changes
- Rename `local_changes` → `get_local_changes` to match Composer's `getLocalChanges` - Add `is_change_report`, `is_vcs_capable_downloader`, `is_dvcs_downloader` trait methods to replace PHP `instanceof` checks - Add `VersionParser` stub to keep `VersionGuesser::new` signature compatible with Composer's constructor - Add `ArrayDumper` in status.rs mirroring `Composer\Package\Dumper\ArrayDumper`; expand `build_package_config` to include all fields that `VersionGuesser` inspects
Diffstat (limited to 'crates/mozart/src/commands/status.rs')
-rw-r--r--crates/mozart/src/commands/status.rs145
1 files changed, 95 insertions, 50 deletions
diff --git a/crates/mozart/src/commands/status.rs b/crates/mozart/src/commands/status.rs
index 60185cb..d15ffb3 100644
--- a/crates/mozart/src/commands/status.rs
+++ b/crates/mozart/src/commands/status.rs
@@ -1,12 +1,11 @@
use crate::composer::Composer;
use clap::Args;
-use indexmap::IndexMap;
use mozart_core::composer::{InstallationSource, LocalPackage};
use mozart_core::console::Console;
use mozart_core::console_writeln;
use mozart_core::console_writeln_error;
use mozart_core::exit_code;
-use mozart_vcs::version_guesser::VersionGuesser;
+use mozart_vcs::version_guesser::{VersionGuesser, VersionParser};
#[derive(Args)]
pub struct StatusArgs {}
@@ -33,11 +32,13 @@ pub async fn execute(
let dm = composer.download_manager();
let im = composer.installation_manager();
- let mut errors = IndexMap::new();
- let mut unpushed_changes = IndexMap::new();
- let mut vcs_version_changes = IndexMap::new();
+ let mut errors = Vec::new();
+ let mut unpushed_changes = Vec::new();
+ let mut vcs_version_changes = Vec::new();
- let guesser = VersionGuesser::new();
+ let parser = VersionParser::new();
+ let guesser = VersionGuesser::new(parser);
+ let dumper = ArrayDumper::new();
for package in installed_repo.get_canonical_packages() {
let Some(downloader) = dm.get_downloader_for_package(package) else {
@@ -46,57 +47,60 @@ pub async fn execute(
let Some(target_dir) = im.get_install_path(package) else {
continue;
};
- let target_key = target_dir.display().to_string();
+ let target_dir_key = target_dir.display().to_string();
- // ChangeReportInterface — Composer mirrors the symlink branch and
- // the local-changes branch unconditionally; the latter overrides
- // the former when both fire.
- if std::fs::symlink_metadata(&target_dir)
- .map(|m| m.file_type().is_symlink())
- .unwrap_or(false)
- {
- errors.insert(
- target_key.clone(),
- format!("{target_key} is a symbolic link."),
- );
- }
- if let Some(changes) = downloader.local_changes(&target_dir)? {
- errors.insert(target_key.clone(), changes);
+ if downloader.is_change_report() {
+ if std::fs::symlink_metadata(&target_dir)
+ .map(|m| m.file_type().is_symlink())
+ .unwrap_or(false)
+ {
+ errors.push((
+ target_dir_key.clone(),
+ format!("{target_dir_key} is a symbolic link."),
+ ));
+ }
+ if let Some(changes) = downloader.get_local_changes(&target_dir)? {
+ errors.push((target_dir_key.clone(), changes));
+ }
}
- // VcsCapableDownloaderInterface
- if downloader.vcs_reference(&target_dir)?.is_some() {
+ if downloader.is_vcs_capable_downloader()
+ && downloader.vcs_reference(&target_dir)?.is_some()
+ {
let previous_ref = match package.installation_source() {
Some(InstallationSource::Source) => package.source_reference(),
Some(InstallationSource::Dist) => package.dist_reference(),
_ => None,
};
- let pkg_config = build_package_config(package);
- let current_version = guesser.guess_version(&pkg_config, &target_dir);
+
+ let current_version = guesser.guess_version(&dumper.dump(package), &target_dir);
+
if let (Some(previous_ref), Some(current_version)) = (previous_ref, current_version) {
- let cur_commit = current_version.commit.as_deref().unwrap_or("");
- let cur_pretty = current_version.pretty_version.as_deref().unwrap_or("");
- if cur_commit != previous_ref && cur_pretty != previous_ref {
- vcs_version_changes.insert(
- target_key.clone(),
+ let current_commit = current_version.commit.as_deref().unwrap_or("");
+ let current_pretty_version =
+ current_version.pretty_version.as_deref().unwrap_or("");
+ if current_commit != previous_ref && current_pretty_version != previous_ref {
+ vcs_version_changes.push((
+ target_dir_key.clone(),
VcsVerChange {
previous: VerRef {
version: package.pretty_version().to_string(),
reference: previous_ref.to_string(),
},
current: VerRef {
- version: cur_pretty.to_string(),
- reference: cur_commit.to_string(),
+ version: current_pretty_version.to_string(),
+ reference: current_commit.to_string(),
},
},
- );
+ ));
}
}
}
- // DvcsDownloaderInterface
- if let Some(unpushed) = downloader.unpushed_changes(&target_dir)? {
- unpushed_changes.insert(target_key.clone(), unpushed);
+ if downloader.is_dvcs_downloader()
+ && let Some(unpushed) = downloader.unpushed_changes(&target_dir)?
+ {
+ unpushed_changes.push((target_dir_key.clone(), unpushed));
}
}
@@ -105,9 +109,6 @@ pub async fn execute(
return Ok(());
}
- let verbose = cli.verbose > 0;
- let very_verbose = cli.verbose >= 2;
-
if !errors.is_empty() {
console_writeln_error!(
console,
@@ -115,7 +116,7 @@ pub async fn execute(
);
for (path, changes) in &errors {
- if verbose {
+ if cli.is_verbose() {
console_writeln!(console, "<info>{path}</info>:");
console_writeln!(console, "{}", &indent_block(changes));
} else {
@@ -131,7 +132,7 @@ pub async fn execute(
);
for (path, changes) in &unpushed_changes {
- if verbose {
+ if cli.is_verbose() {
console_writeln!(console, "<info>{path}</info>:");
console_writeln!(console, "{}", &indent_block(changes));
} else {
@@ -147,7 +148,7 @@ pub async fn execute(
);
for (path, change) in &vcs_version_changes {
- if verbose {
+ if cli.is_verbose() {
let mut prev = if change.previous.version.is_empty() {
change.previous.reference.clone()
} else {
@@ -158,7 +159,7 @@ pub async fn execute(
} else {
change.current.version.clone()
};
- if very_verbose {
+ if console.is_very_verbose() {
prev.push_str(&format!(" ({})", change.previous.reference));
curr.push_str(&format!(" ({})", change.current.reference));
}
@@ -173,7 +174,7 @@ pub async fn execute(
}
}
- if !verbose {
+ if !cli.is_verbose() {
console_writeln_error!(console, "Use --verbose (-v) to see a list of files");
}
@@ -197,11 +198,55 @@ fn indent_block(s: &str) -> String {
.join("\n")
}
-/// Build the `package_config` shape that `VersionGuesser` reads. The PHP
-/// equivalent is `ArrayDumper::dump($package)`; we only need the fields
-/// that `VersionGuesser` actually inspects.
+/// Mirrors `Composer\Package\Dumper\ArrayDumper`. Serialises a `LocalPackage`
+/// into the JSON shape that `VersionGuesser::guess_version` expects.
+struct ArrayDumper;
+
+impl ArrayDumper {
+ fn new() -> Self {
+ Self
+ }
+
+ fn dump(&self, package: &LocalPackage) -> serde_json::Value {
+ build_package_config(package)
+ }
+}
+
+/// Serialises a `LocalPackage` to the JSON shape consumed by
+/// `VersionGuesser::guess_version`. Mirrors `ArrayDumper::dump($package)` —
+/// we include all fields that `VersionGuesser` inspects.
fn build_package_config(package: &LocalPackage) -> serde_json::Value {
- serde_json::json!({
- "extra": package.extra(),
- })
+ let mut obj = serde_json::Map::new();
+ obj.insert("name".into(), package.pretty_name().into());
+ obj.insert("version".into(), package.pretty_version().into());
+ if let Some(t) = package.package_type() {
+ obj.insert("type".into(), t.into());
+ }
+ obj.insert("extra".into(), package.extra().clone());
+ if let Some(src) = package.source() {
+ let mut s = serde_json::Map::new();
+ s.insert("type".into(), src.kind.clone().into());
+ s.insert("url".into(), src.url.clone().into());
+ if let Some(r) = &src.reference {
+ s.insert("reference".into(), r.clone().into());
+ }
+ obj.insert("source".into(), serde_json::Value::Object(s));
+ }
+ if let Some(dist) = package.dist() {
+ let mut d = serde_json::Map::new();
+ d.insert("type".into(), dist.kind.clone().into());
+ d.insert("url".into(), dist.url.clone().into());
+ if let Some(r) = &dist.reference {
+ d.insert("reference".into(), r.clone().into());
+ }
+ obj.insert("dist".into(), serde_json::Value::Object(d));
+ }
+ if let Some(is) = package.installation_source() {
+ let s = match is {
+ InstallationSource::Source => "source",
+ InstallationSource::Dist => "dist",
+ };
+ obj.insert("installation-source".into(), s.into());
+ }
+ serde_json::Value::Object(obj)
}