#![doc(html_root_url = "https://docs.rs/semver-diff/0.1.0")]
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
struct ReadmeDoctests;
use core::cmp::Ordering;
use semver::{BuildMetadata, Version};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Difference {
Major,
PreMajor,
Minor,
PreMinor,
Patch,
PrePatch,
PreRelease,
}
impl Difference {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Difference::Major => "major",
Difference::PreMajor => "premajor",
Difference::Minor => "minor",
Difference::PreMinor => "preminor",
Difference::Patch => "patch",
Difference::PrePatch => "prepatch",
Difference::PreRelease => "prerelease",
}
}
#[must_use]
pub const fn is_prerelease(self) -> bool {
matches!(
self,
Difference::PreMajor
| Difference::PreMinor
| Difference::PrePatch
| Difference::PreRelease
)
}
}
#[must_use]
pub fn diff(a: &Version, b: &Version) -> Option<Difference> {
let ordering = cmp_ignoring_build(a, b);
if ordering == Ordering::Equal {
return None;
}
let (high, low) = if ordering == Ordering::Greater {
(a, b)
} else {
(b, a)
};
let high_has_pre = !high.pre.is_empty();
let low_has_pre = !low.pre.is_empty();
if low_has_pre && !high_has_pre {
if low.minor == 0 && low.patch == 0 {
return Some(Difference::Major);
}
if low.major == high.major && low.minor == high.minor && low.patch == high.patch {
if low.minor != 0 && low.patch == 0 {
return Some(Difference::Minor);
}
return Some(Difference::Patch);
}
}
if a.major != b.major {
return Some(if high_has_pre {
Difference::PreMajor
} else {
Difference::Major
});
}
if a.minor != b.minor {
return Some(if high_has_pre {
Difference::PreMinor
} else {
Difference::Minor
});
}
if a.patch != b.patch {
return Some(if high_has_pre {
Difference::PrePatch
} else {
Difference::Patch
});
}
Some(Difference::PreRelease)
}
fn cmp_ignoring_build(a: &Version, b: &Version) -> Ordering {
if a.build == b.build {
a.cmp(b)
} else {
let a = Version {
build: BuildMetadata::EMPTY,
..a.clone()
};
let b = Version {
build: BuildMetadata::EMPTY,
..b.clone()
};
a.cmp(&b)
}
}
pub fn diff_str(a: &str, b: &str) -> Result<Option<Difference>, semver::Error> {
Ok(diff(&Version::parse(a)?, &Version::parse(b)?))
}