aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-semver
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-02-22 17:49:49 +0900
committernsfisis <nsfisis@gmail.com>2026-02-22 17:49:49 +0900
commit07733b3b328f6e4ec23754fcb3504ddb196d65a3 (patch)
treed3891db598caacf961aa0e4f72a71e766f9ae758 /crates/mozart-semver
parentb696eb7608d921ae0e14a4296e412c33340ceee8 (diff)
downloadphp-mozart-07733b3b328f6e4ec23754fcb3504ddb196d65a3.tar.gz
php-mozart-07733b3b328f6e4ec23754fcb3504ddb196d65a3.tar.zst
php-mozart-07733b3b328f6e4ec23754fcb3504ddb196d65a3.zip
fix(resolver): replace ComposerVersion(u16) with semver::Version(u64)
ComposerVersion used u16 segments (max 65535), causing overflow for PHP extensions like ext-dom (version 20031129). The extension became invisible to the resolver, failing create-project with "depends on ext-dom" errors. Use mozart_semver::Version (u64 segments) directly as pubgrub's version type, eliminating the overflow and redundant conversion layer. Also fixes Ord for patch pre-releases (patch1 > stable) and adds Display impl required by pubgrub. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-semver')
-rw-r--r--crates/mozart-semver/src/lib.rs59
1 files changed, 44 insertions, 15 deletions
diff --git a/crates/mozart-semver/src/lib.rs b/crates/mozart-semver/src/lib.rs
index 7c417ec..474b6ce 100644
--- a/crates/mozart-semver/src/lib.rs
+++ b/crates/mozart-semver/src/lib.rs
@@ -1,4 +1,5 @@
use std::cmp::Ordering;
+use std::fmt;
/// A parsed Composer version (always 4 numeric segments + optional stability suffix).
/// Composer normalizes all versions to `major.minor.patch.build[-stability[N]]`.
@@ -70,11 +71,26 @@ impl Ord for Version {
return num_cmp;
}
- // Compare pre-release: None (stable) > any pre-release
+ // Compare pre-release: None (stable) > most pre-releases,
+ // but patch/pl/p pre-releases (stability_rank 5) rank ABOVE stable.
match (&self.pre_release, &other.pre_release) {
(None, None) => Ordering::Equal,
- (None, Some(_)) => Ordering::Greater,
- (Some(_), None) => Ordering::Less,
+ (None, Some(b)) => {
+ if stability_rank(b) == 5 {
+ // patch pre-release ranks above stable
+ Ordering::Less
+ } else {
+ Ordering::Greater
+ }
+ }
+ (Some(a), None) => {
+ if stability_rank(a) == 5 {
+ // patch pre-release ranks above stable
+ Ordering::Greater
+ } else {
+ Ordering::Less
+ }
+ }
(Some(a), Some(b)) => {
let rank_a = stability_rank(a);
let rank_b = stability_rank(b);
@@ -92,6 +108,23 @@ impl Ord for Version {
}
}
+impl fmt::Display for Version {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.is_dev_branch {
+ if let Some(ref name) = self.dev_branch_name {
+ return write!(f, "dev-{}", name);
+ }
+ // Numeric dev branch (e.g. "2.x-dev")
+ return write!(f, "{}.{}.{}.{}-dev", self.major, self.minor, self.patch, self.build);
+ }
+ write!(f, "{}.{}.{}.{}", self.major, self.minor, self.patch, self.build)?;
+ if let Some(ref pre) = self.pre_release {
+ write!(f, "-{}", pre)?;
+ }
+ Ok(())
+ }
+}
+
impl Version {
/// Parse a version string into a `Version` struct using Composer normalization rules.
///
@@ -1224,14 +1257,12 @@ mod tests {
}
#[test]
- fn test_ordering_stable_lt_patch() {
- // The Ord impl: (None, Some(_)) => Greater — stable (pre_release=None) beats any
- // pre_release including "patch1". Even though stability_rank("patch")=5 which is
- // higher than stable's implicit 0, that path is only reached when both sides are
- // Some(_). Since stable has pre_release=None, stable > patch version.
+ fn test_ordering_patch_gt_stable() {
+ // In Composer, patch/pl/p pre-releases rank ABOVE stable.
+ // e.g. 1.0.0-patch1 > 1.0.0
let stable = Version::parse("1.0.0").unwrap();
let patch = Version::parse("1.0.0-patch1").unwrap();
- assert!(stable > patch);
+ assert!(patch > stable);
}
#[test]
@@ -1757,15 +1788,13 @@ mod tests {
#[test]
fn test_stability_rank_patch_via_ordering() {
- // The Ord impl: (None, Some(_)) => Greater.
- // stable has pre_release=None; patch version has pre_release=Some("patch1").
- // The None arm wins unconditionally: stable is always Greater than any pre_release.
- // This means "patch" releases (post-release fixes) sort BELOW stable in this impl.
+ // In Composer, patch/pl/p pre-releases rank ABOVE stable.
+ // patch1 > stable for the same numeric version.
let patch_ver = Version::parse("1.0.0-patch1").unwrap();
let stable = Version::parse("1.0.0").unwrap();
assert!(
- stable > patch_ver,
- "stable (None pre_release) beats patch pre-release"
+ patch_ver > stable,
+ "patch pre-release ranks above stable"
);
}