diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-08 19:52:18 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-08 19:52:18 +0900 |
| commit | 5cb8fc4e306970764e84bb850da2c56f844c3b12 (patch) | |
| tree | 0d66f3129a26138fcfee9402616b24929c40a017 /crates/mozart-semver/src | |
| parent | d83b9ef48775aeb31ba1909b29d5470e6d0ddaaa (diff) | |
| download | php-mozart-5cb8fc4e306970764e84bb850da2c56f844c3b12.tar.gz php-mozart-5cb8fc4e306970764e84bb850da2c56f844c3b12.tar.zst php-mozart-5cb8fc4e306970764e84bb850da2c56f844c3b12.zip | |
fix(status): align with Composer's StatusCommand pipeline
Replace the dist-hash tree-diff implementation with Composer's VCS-level
status flow: three buckets (errors / unpushed_changes / vcs_version_changes)
populated via ChangeReportInterface / DvcsDownloaderInterface /
VcsCapableDownloaderInterface, and a bitfield exit code (1|2|4) instead
of always 1.
Supporting work:
- mozart-semver: add normalize_branch (VersionParser::normalizeBranch).
- mozart-vcs: extend VcsDownloader trait with unpushed_changes /
vcs_reference; port GitDownloader::getUnpushedChanges (HEAD-ref
discovery + git diff --name-status remote...branch + two-pass fetch);
fix git status invocation to use --untracked-files=no (Composer parity);
add hasMetadataRepository preconditions to git/hg/svn local_changes;
port VersionGuesser (git/hg/svn dispatch — Fossil omitted, feature
branch detection runs sequentially instead of via async promises).
- mozart-core: extend LocalPackage with pretty_version, package_type,
installation_source, source, dist, extra; add InstallationSource and
PackageReference. factory.rs reads them from installed.json.
- mozart-registry: new download_manager mirroring
DownloadManager::getDownloaderForPackage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-semver/src')
| -rw-r--r-- | crates/mozart-semver/src/lib.rs | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/crates/mozart-semver/src/lib.rs b/crates/mozart-semver/src/lib.rs index bad1690..013579d 100644 --- a/crates/mozart-semver/src/lib.rs +++ b/crates/mozart-semver/src/lib.rs @@ -954,6 +954,63 @@ fn hyphen_upper_bound(raw: &str) -> Result<VersionConstraint, String> { Ok(VersionConstraint::Single(Constraint::LessThan(next))) } +/// Normalize a branch name into a normalized "X.Y.Z.W-dev" form, mirroring +/// `Composer\Semver\VersionParser::normalizeBranch`. Numeric branches like +/// `1.0` or `2.x` are zero-padded out to four segments with `9999999` +/// substituted for `x`/`X`/`*`. Any other shape (e.g. `main`, `feat/x`) +/// becomes `dev-<branch>`. +pub fn normalize_branch(name: &str) -> String { + let trimmed = name.trim(); + + let stripped = trimmed + .strip_prefix('v') + .or_else(|| trimmed.strip_prefix('V')) + .unwrap_or(trimmed); + + if stripped.is_empty() { + return format!("dev-{name}"); + } + + let parts: Vec<&str> = stripped.split('.').collect(); + if parts.len() > 4 { + return format!("dev-{name}"); + } + + if parts[0].is_empty() || !parts[0].chars().all(|c| c.is_ascii_digit()) { + return format!("dev-{name}"); + } + for seg in &parts[1..] { + if seg.is_empty() { + return format!("dev-{name}"); + } + let all_digits = seg.chars().all(|c| c.is_ascii_digit()); + let single_wildcard = + seg.len() == 1 && matches!(seg.chars().next().unwrap(), 'x' | 'X' | '*'); + if !all_digits && !single_wildcard { + return format!("dev-{name}"); + } + } + + let mut out = String::with_capacity(stripped.len() + 32); + out.push_str(parts[0]); + for i in 1..4 { + out.push('.'); + match parts.get(i) { + None => out.push_str("9999999"), + Some(seg) => { + let first = seg.chars().next().unwrap(); + if seg.len() == 1 && matches!(first, 'x' | 'X' | '*') { + out.push_str("9999999"); + } else { + out.push_str(seg); + } + } + } + } + out.push_str("-dev"); + out +} + #[cfg(test)] mod tests { use super::*; @@ -2346,4 +2403,51 @@ mod tests { let b = VersionConstraint::parse(">=2.0 <=3.0").unwrap(); assert!(a.intersects(&b)); } + + #[test] + fn test_normalize_branch_numeric_full() { + assert_eq!(normalize_branch("1.0.0"), "1.0.0.9999999-dev"); + assert_eq!(normalize_branch("1.0.0.5"), "1.0.0.5-dev"); + } + + #[test] + fn test_normalize_branch_numeric_short() { + assert_eq!(normalize_branch("1"), "1.9999999.9999999.9999999-dev"); + assert_eq!(normalize_branch("1.0"), "1.0.9999999.9999999-dev"); + } + + #[test] + fn test_normalize_branch_wildcards() { + assert_eq!(normalize_branch("2.x"), "2.9999999.9999999.9999999-dev"); + assert_eq!(normalize_branch("1.0.x"), "1.0.9999999.9999999-dev"); + assert_eq!(normalize_branch("1.0.X"), "1.0.9999999.9999999-dev"); + assert_eq!(normalize_branch("1.0.*"), "1.0.9999999.9999999-dev"); + } + + #[test] + fn test_normalize_branch_v_prefix() { + assert_eq!(normalize_branch("v1.2"), "1.2.9999999.9999999-dev"); + assert_eq!(normalize_branch("V2"), "2.9999999.9999999.9999999-dev"); + } + + #[test] + fn test_normalize_branch_non_numeric() { + assert_eq!(normalize_branch("master"), "dev-master"); + assert_eq!(normalize_branch("main"), "dev-main"); + assert_eq!(normalize_branch("feature/x"), "dev-feature/x"); + assert_eq!(normalize_branch("1.0-beta"), "dev-1.0-beta"); + } + + #[test] + fn test_normalize_branch_trims_input() { + assert_eq!(normalize_branch(" 1.0 "), "1.0.9999999.9999999-dev"); + } + + #[test] + fn test_normalize_branch_empty_or_invalid_segments() { + assert_eq!(normalize_branch(""), "dev-"); + assert_eq!(normalize_branch("1."), "dev-1."); + assert_eq!(normalize_branch("1.0.0.0.0"), "dev-1.0.0.0.0"); + assert_eq!(normalize_branch("xx"), "dev-xx"); + } } |
