diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-22 12:43:46 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-22 12:43:46 +0900 |
| commit | f2386320d1934f7e52b4cda36d19c86c239423b0 (patch) | |
| tree | 68d55589be4be2f6bc423c02fa87778fcbf3740a /crates/mozart/src/version_bumper.rs | |
| parent | 1ef1ebdcf50ae1358ec06e3c6a2fb797a8461617 (diff) | |
| download | php-mozart-f2386320d1934f7e52b4cda36d19c86c239423b0.tar.gz php-mozart-f2386320d1934f7e52b4cda36d19c86c239423b0.tar.zst php-mozart-f2386320d1934f7e52b4cda36d19c86c239423b0.zip | |
chore: remove unused files
Diffstat (limited to 'crates/mozart/src/version_bumper.rs')
| -rw-r--r-- | crates/mozart/src/version_bumper.rs | 667 |
1 files changed, 0 insertions, 667 deletions
diff --git a/crates/mozart/src/version_bumper.rs b/crates/mozart/src/version_bumper.rs deleted file mode 100644 index 43c21d6..0000000 --- a/crates/mozart/src/version_bumper.rs +++ /dev/null @@ -1,667 +0,0 @@ -/// Version constraint bumper. -/// -/// Given a constraint string (from composer.json) and the installed version -/// (from composer.lock), computes a new constraint string that raises the -/// lower bound to match the installed version. -/// -/// Returns `None` if no change is needed, or `Some(new_constraint)` if the -/// constraint should be updated. -pub fn bump_requirement( - constraint_str: &str, - pretty_version: &str, - version_normalized: Option<&str>, -) -> Option<String> { - let constraint = constraint_str.trim(); - - // Strip and preserve stability flag (@dev, @beta, etc.) - let (constraint_body, stability_flag) = strip_stability_flag(constraint); - - // Dev constraints (dev-master, dev-main, etc.) are left unchanged - if constraint_body.trim().starts_with("dev-") { - return None; - } - - // Skip dev installed versions that have no alias - // An alias looks like "dev-master as 1.0.0" — the version string in the lock - // would be "dev-master" without " as ". - if pretty_version.starts_with("dev-") && !pretty_version.contains(" as ") { - return None; - } - if let Some(norm) = version_normalized - && norm.starts_with("dev-") - && !pretty_version.contains(" as ") - { - return None; - } - - // Resolve the actual version string to use for bumping. - // If the pretty_version contains an inline alias (e.g. "dev-master as 1.0.0"), - // take the alias target. Otherwise use pretty_version directly. - let installed_version = resolve_installed_version(pretty_version, version_normalized); - - // Handle OR constraints (^1.0 || ^2.0) - if constraint_body.contains("||") { - return bump_or_constraint(constraint_body, &installed_version, stability_flag); - } - - // Single constraint - bump_single(constraint_body.trim(), &installed_version, stability_flag) -} - -// ─── OR constraint handling ─────────────────────────────────────────────────── - -fn bump_or_constraint( - constraint_body: &str, - installed_version: &str, - stability_flag: Option<&str>, -) -> Option<String> { - let parts: Vec<&str> = constraint_body.split("||").map(str::trim).collect(); - - // Determine which major the installed version belongs to - let installed_major = parse_major(installed_version); - - let mut changed = false; - let mut new_parts: Vec<String> = Vec::new(); - - for part in &parts { - let part_trimmed = part.trim(); - // Determine the major range this disjunct covers - let part_major = constraint_major(part_trimmed); - - // Only bump the disjunct whose major matches the installed version's major - if part_major == installed_major { - if let Some(bumped) = bump_single(part_trimmed, installed_version, None) { - new_parts.push(bumped); - changed = true; - } else { - new_parts.push(part_trimmed.to_string()); - } - } else { - new_parts.push(part_trimmed.to_string()); - } - } - - if !changed { - return None; - } - - let joined = new_parts.join(" || "); - let result = append_stability_flag(&joined, stability_flag); - Some(result) -} - -// ─── Single constraint handling ─────────────────────────────────────────────── - -fn bump_single( - constraint: &str, - installed_version: &str, - stability_flag: Option<&str>, -) -> Option<String> { - // AND constraints (space-separated multiple operators like ">=1.0 <2.0" or - // comma-separated like ">=1.0,<2.0") are not supported for bumping — leave unchanged. - // We detect them by checking for a space or comma after the version spec begins. - // Quick check: if the constraint contains a space (ignoring leading operators), - // it's likely a multi-part AND constraint. - let after_op = constraint - .trim_start_matches('^') - .trim_start_matches('~') - .trim_start_matches(">=") - .trim_start_matches("<=") - .trim_start_matches("!=") - .trim_start_matches('>') - .trim_start_matches('<') - .trim_start_matches('='); - if after_op.contains(' ') || after_op.contains(',') { - return None; - } - - // Caret: ^X.Y.Z - if let Some(rest) = constraint.strip_prefix('^') { - return bump_caret(rest.trim(), installed_version, stability_flag); - } - - // Tilde: ~X.Y.Z - if let Some(rest) = constraint.strip_prefix('~') { - return bump_tilde(rest.trim(), installed_version, stability_flag); - } - - // Wildcard: * or X.* - if constraint == "*" || constraint.ends_with(".*") { - return bump_wildcard(constraint, installed_version, stability_flag); - } - - // Greater-or-equal: >=X.Y - if let Some(rest) = constraint.strip_prefix(">=") { - return bump_gte(rest.trim(), installed_version, stability_flag); - } - - // Other operators (exact, <, <=, >, !=, range) — leave unchanged - None -} - -// ─── Caret bump ─────────────────────────────────────────────────────────────── - -/// `^X.Y.Z` → bump to installed version if it is greater. -/// -/// The caret prefix is preserved; segments from installed version replace -/// those in the constraint (trimming trailing zeros appropriately). -fn bump_caret(rest: &str, installed_version: &str, stability_flag: Option<&str>) -> Option<String> { - let constraint_segments = parse_version_segments(rest); - let installed_segments = parse_version_segments(installed_version); - - // The constraint length determines how many segments to compare/output - let n_constraint = constraint_segments.len().max(1); - - // Compare: if installed <= current lower bound, no change needed - // We compare as many segments as the installed version has - let current_lower: Vec<u64> = constraint_segments - .iter() - .copied() - .chain(std::iter::repeat(0)) - .take(4) - .collect(); - let installed: Vec<u64> = installed_segments - .iter() - .copied() - .chain(std::iter::repeat(0)) - .take(4) - .collect(); - - if installed <= current_lower { - return None; - } - - // Build new constraint segments: use installed version, but only up to - // the number of non-trivial segments needed. - // We output at least as many segments as the original constraint had, - // but trim trailing zeros. - let mut new_segs: Vec<u64> = installed_segments - .iter() - .copied() - .chain(std::iter::repeat(0)) - .take(n_constraint.max(installed_segments.len())) - .collect(); - - // Trim trailing zeros (but keep at least n_constraint segments, minimum 1) - while new_segs.len() > n_constraint && new_segs.last() == Some(&0) { - new_segs.pop(); - } - // Also trim trailing zeros beyond 1 segment - while new_segs.len() > 1 && new_segs.last() == Some(&0) { - new_segs.pop(); - } - - let version_str = new_segs - .iter() - .map(|n| n.to_string()) - .collect::<Vec<_>>() - .join("."); - - let new_constraint = format!("^{version_str}"); - let result = append_stability_flag(&new_constraint, stability_flag); - Some(result) -} - -// ─── Tilde bump ─────────────────────────────────────────────────────────────── - -/// `~X.Y.Z` (3 segments) → bump patch: `~X.Y.new_patch` -/// `~X.Y` (2 segments) → convert to caret: `^X.Y.new_patch` -fn bump_tilde(rest: &str, installed_version: &str, stability_flag: Option<&str>) -> Option<String> { - let constraint_segments = parse_version_segments(rest); - let installed_segments = parse_version_segments(installed_version); - - let current_lower: Vec<u64> = constraint_segments - .iter() - .copied() - .chain(std::iter::repeat(0)) - .take(4) - .collect(); - let installed: Vec<u64> = installed_segments - .iter() - .copied() - .chain(std::iter::repeat(0)) - .take(4) - .collect(); - - if installed <= current_lower { - return None; - } - - let major = installed_segments.first().copied().unwrap_or(0); - let minor = installed_segments.get(1).copied().unwrap_or(0); - let patch = installed_segments.get(2).copied().unwrap_or(0); - - let new_constraint = if constraint_segments.len() >= 3 { - // ~X.Y.Z → keep tilde, bump patch - if patch == 0 { - format!("~{major}.{minor}.0") - } else { - format!("~{major}.{minor}.{patch}") - } - } else { - // ~X.Y → convert to caret - if patch == 0 { - format!("^{major}.{minor}") - } else { - format!("^{major}.{minor}.{patch}") - } - }; - - let result = append_stability_flag(&new_constraint, stability_flag); - Some(result) -} - -// ─── Wildcard bump ──────────────────────────────────────────────────────────── - -/// `*` → `>=installed` -/// `X.*` → `>=installed` (trimming trailing zeros) -fn bump_wildcard( - constraint: &str, - installed_version: &str, - stability_flag: Option<&str>, -) -> Option<String> { - let installed_segments = parse_version_segments(installed_version); - - // Trim trailing zeros - let mut segs = installed_segments.clone(); - while segs.len() > 1 && segs.last() == Some(&0) { - segs.pop(); - } - - let version_str = segs - .iter() - .map(|n| n.to_string()) - .collect::<Vec<_>>() - .join("."); - - // For plain wildcard "*", always produce >=installed - if constraint == "*" { - let new_constraint = format!(">={version_str}"); - return Some(append_stability_flag(&new_constraint, stability_flag)); - } - - // For "X.*", if installed is at that major, produce >=installed - let base = constraint.trim_end_matches(".*"); - let base_segs = parse_version_segments(base); - let current_lower: Vec<u64> = base_segs - .iter() - .copied() - .chain(std::iter::repeat(0)) - .take(4) - .collect(); - let installed: Vec<u64> = installed_segments - .iter() - .copied() - .chain(std::iter::repeat(0)) - .take(4) - .collect(); - - if installed <= current_lower { - return None; - } - - let new_constraint = format!(">={version_str}"); - Some(append_stability_flag(&new_constraint, stability_flag)) -} - -// ─── GTE bump ───────────────────────────────────────────────────────────────── - -/// `>=X.Y` → raise to installed version (trimming trailing zeros) -fn bump_gte(rest: &str, installed_version: &str, stability_flag: Option<&str>) -> Option<String> { - let constraint_segments = parse_version_segments(rest); - let installed_segments = parse_version_segments(installed_version); - - let current_lower: Vec<u64> = constraint_segments - .iter() - .copied() - .chain(std::iter::repeat(0)) - .take(4) - .collect(); - let installed: Vec<u64> = installed_segments - .iter() - .copied() - .chain(std::iter::repeat(0)) - .take(4) - .collect(); - - if installed <= current_lower { - return None; - } - - // Trim trailing zeros from installed version - let mut segs = installed_segments.clone(); - while segs.len() > 1 && segs.last() == Some(&0) { - segs.pop(); - } - - let version_str = segs - .iter() - .map(|n| n.to_string()) - .collect::<Vec<_>>() - .join("."); - - let new_constraint = format!(">={version_str}"); - let result = append_stability_flag(&new_constraint, stability_flag); - Some(result) -} - -// ─── Helpers ────────────────────────────────────────────────────────────────── - -/// Strip a trailing `@stability` flag from a constraint string. -/// Returns (body, flag) where flag is the `@...` suffix (without the `@`). -fn strip_stability_flag(constraint: &str) -> (&str, Option<&str>) { - let known = ["@dev", "@alpha", "@beta", "@RC", "@rc", "@stable"]; - for flag in &known { - if let Some(body) = constraint.strip_suffix(flag) { - let flag_str = &constraint[body.len()..]; - return (body.trim_end(), Some(flag_str)); - } - } - (constraint, None) -} - -/// Append an optional stability flag to a constraint string. -fn append_stability_flag(constraint: &str, flag: Option<&str>) -> String { - match flag { - Some(f) => format!("{constraint}{f}"), - None => constraint.to_string(), - } -} - -/// Parse a version string into numeric segments. -/// Handles "1.2.3", "1.2", "1", etc. -/// Stops at any non-numeric/non-dot character. -fn parse_version_segments(version: &str) -> Vec<u64> { - // Strip inline alias: "dev-master as 1.0.0" → "1.0.0" - let version = if let Some(pos) = version.find(" as ") { - &version[pos + 4..] - } else { - version - }; - - // Strip leading v/V - let version = version - .strip_prefix('v') - .or_else(|| version.strip_prefix('V')) - .unwrap_or(version); - - // Take up to any pre-release suffix (first '-' or '+') - let version = version.split(['-', '+']).next().unwrap_or(version); - - version - .split('.') - .filter_map(|s| s.parse::<u64>().ok()) - .collect() -} - -/// Parse the major version number from a version string. -fn parse_major(version: &str) -> Option<u64> { - parse_version_segments(version).into_iter().next() -} - -/// Determine the major version that a single disjunct constraint covers. -/// For `^1.2`, returns `Some(1)`. For `^0.3`, returns `Some(0)`. -fn constraint_major(constraint: &str) -> Option<u64> { - if let Some(rest) = constraint.strip_prefix('^') { - return parse_version_segments(rest).into_iter().next(); - } - if let Some(rest) = constraint.strip_prefix('~') { - return parse_version_segments(rest).into_iter().next(); - } - if let Some(rest) = constraint.strip_prefix(">=") { - return parse_version_segments(rest).into_iter().next(); - } - // Try as plain version - parse_version_segments(constraint).into_iter().next() -} - -/// Resolve the installed version string to use for comparison. -/// Handles inline aliases (e.g., "dev-main as 2.1.0" → "2.1.0"). -fn resolve_installed_version<'a>( - pretty_version: &'a str, - _version_normalized: Option<&'a str>, -) -> String { - // If pretty_version contains an inline alias, use the alias target - if let Some(pos) = pretty_version.find(" as ") { - return pretty_version[pos + 4..].trim().to_string(); - } - - // If version_normalized is available and not a dev branch, prefer it - // for more precise comparison, but use pretty_version for output - // Actually we use pretty_version for building constraint strings - // since normalized versions have extra .0 suffixes - - // Use pretty_version as-is (strip leading 'v' for normalization) - pretty_version - .strip_prefix('v') - .unwrap_or(pretty_version) - .to_string() -} - -// ─── Tests ──────────────────────────────────────────────────────────────────── - -#[cfg(test)] -mod tests { - use super::*; - - // ── Caret bumps ─────────────────────────────────────────────────────────── - - #[test] - fn test_caret_bump_basic() { - // ^1.0 + 1.2.1 → ^1.2.1 - let result = bump_requirement("^1.0", "1.2.1", Some("1.2.1.0")); - assert_eq!(result, Some("^1.2.1".to_string())); - } - - #[test] - fn test_caret_no_change_at_lower_bound() { - // ^1.2 + 1.2.0 → None (already at lower bound) - let result = bump_requirement("^1.2", "1.2.0", Some("1.2.0.0")); - assert_eq!(result, None); - } - - #[test] - fn test_caret_no_change_exact_match() { - // ^1.2.1 + 1.2.1 → None - let result = bump_requirement("^1.2.1", "1.2.1", Some("1.2.1.0")); - assert_eq!(result, None); - } - - #[test] - fn test_caret_bump_zero_major() { - // ^0.3 + 0.3.5 → ^0.3.5 - let result = bump_requirement("^0.3", "0.3.5", Some("0.3.5.0")); - assert_eq!(result, Some("^0.3.5".to_string())); - } - - #[test] - fn test_caret_bump_three_segments() { - // ^1.0.0 + 1.2.1 → ^1.2.1 - let result = bump_requirement("^1.0.0", "1.2.1", Some("1.2.1.0")); - assert_eq!(result, Some("^1.2.1".to_string())); - } - - #[test] - fn test_caret_bump_minor_only() { - // ^1.2 + 1.5.0 → ^1.5 (trailing zero trimmed) - let result = bump_requirement("^1.2", "1.5.0", Some("1.5.0.0")); - assert_eq!(result, Some("^1.5".to_string())); - } - - // ── Tilde bumps ─────────────────────────────────────────────────────────── - - #[test] - fn test_tilde_three_segment_bump() { - // ~2.0.0 + 2.0.3 → ~2.0.3 - let result = bump_requirement("~2.0.0", "2.0.3", Some("2.0.3.0")); - assert_eq!(result, Some("~2.0.3".to_string())); - } - - #[test] - fn test_tilde_two_segment_becomes_caret() { - // ~2.0 + 2.0.3 → ^2.0.3 - let result = bump_requirement("~2.0", "2.0.3", Some("2.0.3.0")); - assert_eq!(result, Some("^2.0.3".to_string())); - } - - #[test] - fn test_tilde_no_change() { - // ~2.0.3 + 2.0.3 → None - let result = bump_requirement("~2.0.3", "2.0.3", Some("2.0.3.0")); - assert_eq!(result, None); - } - - #[test] - fn test_tilde_two_segment_no_patch() { - // ~2.3 + 2.5.0 → ^2.5 (patch is 0, trimmed) - let result = bump_requirement("~2.3", "2.5.0", Some("2.5.0.0")); - assert_eq!(result, Some("^2.5".to_string())); - } - - // ── Wildcard bumps ──────────────────────────────────────────────────────── - - #[test] - fn test_wildcard_star() { - // * + 1.2.3 → >=1.2.3 - let result = bump_requirement("*", "1.2.3", Some("1.2.3.0")); - assert_eq!(result, Some(">=1.2.3".to_string())); - } - - #[test] - fn test_wildcard_major_star() { - // 2.* + 2.5.0 → >=2.5 - let result = bump_requirement("2.*", "2.5.0", Some("2.5.0.0")); - assert_eq!(result, Some(">=2.5".to_string())); - } - - #[test] - fn test_wildcard_no_change() { - // 2.* + 2.0.0 → None (installed is at lower bound) - let result = bump_requirement("2.*", "2.0.0", Some("2.0.0.0")); - assert_eq!(result, None); - } - - // ── GTE bumps ───────────────────────────────────────────────────────────── - - #[test] - fn test_gte_bump() { - // >=1.2 + 1.5.0 → >=1.5 - let result = bump_requirement(">=1.2", "1.5.0", Some("1.5.0.0")); - assert_eq!(result, Some(">=1.5".to_string())); - } - - #[test] - fn test_gte_no_change() { - // >=1.5 + 1.5.0 → None - let result = bump_requirement(">=1.5", "1.5.0", Some("1.5.0.0")); - assert_eq!(result, None); - } - - #[test] - fn test_gte_with_patch() { - // >=1.2.0 + 1.5.3 → >=1.5.3 - let result = bump_requirement(">=1.2.0", "1.5.3", Some("1.5.3.0")); - assert_eq!(result, Some(">=1.5.3".to_string())); - } - - // ── OR constraints ──────────────────────────────────────────────────────── - - #[test] - fn test_or_constraint_bumps_matching_major() { - // ^1.2 || ^2.3 + 1.3.0 → ^1.3 || ^2.3 - let result = bump_requirement("^1.2 || ^2.3", "1.3.0", Some("1.3.0.0")); - assert_eq!(result, Some("^1.3 || ^2.3".to_string())); - } - - #[test] - fn test_or_constraint_bumps_second_major() { - // ^1.2 || ^2.3 + 2.5.0 → ^1.2 || ^2.5 - let result = bump_requirement("^1.2 || ^2.3", "2.5.0", Some("2.5.0.0")); - assert_eq!(result, Some("^1.2 || ^2.5".to_string())); - } - - #[test] - fn test_or_constraint_no_change() { - // ^1.2 || ^2.3 + 1.2.0 → None - let result = bump_requirement("^1.2 || ^2.3", "1.2.0", Some("1.2.0.0")); - assert_eq!(result, None); - } - - // ── Dev constraints ─────────────────────────────────────────────────────── - - #[test] - fn test_dev_constraint_unchanged() { - // dev-master → None - let result = bump_requirement("dev-master", "dev-master", None); - assert_eq!(result, None); - } - - #[test] - fn test_dev_installed_no_alias_unchanged() { - // Installed is dev-main without alias → None - let result = bump_requirement("^1.0", "dev-main", None); - assert_eq!(result, None); - } - - #[test] - fn test_dev_installed_with_alias() { - // Installed is "dev-main as 1.2.0" → bump based on alias - let result = bump_requirement("^1.0", "dev-main as 1.2.0", None); - assert_eq!(result, Some("^1.2".to_string())); - } - - // ── Stability flags ─────────────────────────────────────────────────────── - - #[test] - fn test_stability_flag_preserved() { - // ^1.0@dev + 1.2.0 → ^1.2@dev - let result = bump_requirement("^1.0@dev", "1.2.0", Some("1.2.0.0")); - assert_eq!(result, Some("^1.2@dev".to_string())); - } - - #[test] - fn test_stability_flag_beta_preserved() { - // ^1.0@beta + 1.2.1 → ^1.2.1@beta - let result = bump_requirement("^1.0@beta", "1.2.1", Some("1.2.1.0")); - assert_eq!(result, Some("^1.2.1@beta".to_string())); - } - - // ── Edge cases ──────────────────────────────────────────────────────────── - - #[test] - fn test_exact_constraint_no_bump() { - // 1.2.3 → None (exact version, not bumped) - let result = bump_requirement("1.2.3", "1.3.0", Some("1.3.0.0")); - assert_eq!(result, None); - } - - #[test] - fn test_complex_range_no_bump() { - // >=1.0 <2.0 → None (complex range, not bumped) - let result = bump_requirement(">=1.0 <2.0", "1.5.0", Some("1.5.0.0")); - assert_eq!(result, None); - } - - #[test] - fn test_parse_version_segments_basic() { - assert_eq!(parse_version_segments("1.2.3"), vec![1, 2, 3]); - assert_eq!(parse_version_segments("1.2"), vec![1, 2]); - assert_eq!(parse_version_segments("1"), vec![1]); - } - - #[test] - fn test_parse_version_segments_with_prerelease() { - assert_eq!(parse_version_segments("1.2.3-beta1"), vec![1, 2, 3]); - } - - #[test] - fn test_parse_version_segments_with_v_prefix() { - assert_eq!(parse_version_segments("v1.2.3"), vec![1, 2, 3]); - } - - #[test] - fn test_parse_version_segments_alias() { - // "dev-master as 1.0.0" → segments of "1.0.0" - assert_eq!(parse_version_segments("dev-master as 1.0.0"), vec![1, 0, 0]); - } -} |
