mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-09-30 21:34:53 +02:00
This PR sticks a warning on all types that are used when serializing Krill’s state. This hopefully will serve as a reminder that they cannot be changed without considering migrations.
313 lines
9.4 KiB
Rust
313 lines
9.4 KiB
Rust
use std::{cmp::Ordering, fmt, str::FromStr};
|
|
|
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
use clap::crate_version;
|
|
|
|
|
|
//------------ KrillVersion --------------------------------------------------
|
|
|
|
/// Defines a Krill version.
|
|
// *Warning:* This type is used in stored state.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct KrillVersion {
|
|
major: u64,
|
|
minor: u64,
|
|
patch: u64,
|
|
release_type: KrillVersionReleaseType,
|
|
}
|
|
|
|
impl KrillVersion {
|
|
pub fn code_version() -> Self {
|
|
// Note: we have a unit test to ensure that the KRILL_VERSION constant
|
|
// which is derived from the Cargo.toml version can be parsed.
|
|
Self::from_str(crate_version!()).unwrap()
|
|
}
|
|
|
|
/// Make a notation friendly to namespaces for upgrades.
|
|
pub fn hyphen_notated(&self) -> String {
|
|
format!(
|
|
"{}-{}-{}{}",
|
|
self.major, self.minor, self.patch, self.release_type
|
|
)
|
|
}
|
|
|
|
pub fn v0_5_0_or_before() -> Self {
|
|
Self::dev(0, 5, 0, "or-before".to_string())
|
|
}
|
|
|
|
pub fn release(major: u64, minor: u64, patch: u64) -> Self {
|
|
KrillVersion {
|
|
major,
|
|
minor,
|
|
patch,
|
|
release_type: KrillVersionReleaseType::Release,
|
|
}
|
|
}
|
|
|
|
pub fn candidate(
|
|
major: u64,
|
|
minor: u64,
|
|
patch: u64,
|
|
number: u64,
|
|
) -> Self {
|
|
KrillVersion {
|
|
major,
|
|
minor,
|
|
patch,
|
|
release_type: KrillVersionReleaseType::Candidate(number),
|
|
}
|
|
}
|
|
|
|
pub fn dev(major: u64, minor: u64, patch: u64, addition: String) -> Self {
|
|
KrillVersion {
|
|
major,
|
|
minor,
|
|
patch,
|
|
release_type: KrillVersionReleaseType::Dev(addition),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromStr for KrillVersion {
|
|
type Err = KrillVersionParseError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
// x.y.z => major.minor.patch release
|
|
// x.y.z-rc# => major.minor.patch release candidate #
|
|
// x.y.z-<str> => major.minor.patch dev 'str'
|
|
// other => cannot parse
|
|
//
|
|
// Support legacy enum based version notation as well:
|
|
// V0_6 => 0.6.0
|
|
|
|
let parts: Vec<&str> = s.split('.').collect();
|
|
if parts.len() == 3 {
|
|
let major = u64::from_str(parts[0])
|
|
.map_err(|_| KrillVersionParseError::for_str(s))?;
|
|
|
|
let minor = u64::from_str(parts[1])
|
|
.map_err(|_| KrillVersionParseError::for_str(s))?;
|
|
|
|
let mut patch_parts = parts[2].split('-');
|
|
|
|
let patch = u64::from_str(patch_parts.next().unwrap())
|
|
.map_err(|_| KrillVersionParseError::for_str(s))?;
|
|
|
|
match patch_parts.next() {
|
|
None => Ok(KrillVersion::release(major, minor, patch)),
|
|
Some(addition) => {
|
|
if addition.len() > 2 && addition.starts_with("rc") {
|
|
let number =
|
|
u64::from_str(&addition[2..]).map_err(|_| {
|
|
KrillVersionParseError::for_str(s)
|
|
})?;
|
|
Ok(KrillVersion::candidate(
|
|
major, minor, patch, number,
|
|
))
|
|
} else {
|
|
Ok(KrillVersion::dev(
|
|
major,
|
|
minor,
|
|
patch,
|
|
addition.to_string(),
|
|
))
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
match s {
|
|
// Enums present in versions before 0.9.1
|
|
"V0_6" => Ok(KrillVersion::release(0, 6, 0)),
|
|
"V0_7" => Ok(KrillVersion::release(0, 7, 0)),
|
|
"V0_8_0_RC1" => Ok(KrillVersion::candidate(0, 8, 0, 1)),
|
|
"V0_8" => Ok(KrillVersion::release(0, 8, 0)),
|
|
"V0_8_1_RC1" => Ok(KrillVersion::candidate(0, 8, 1, 1)),
|
|
"V0_8_1" => Ok(KrillVersion::release(0, 8, 1)),
|
|
"V0_8_2" => Ok(KrillVersion::release(0, 8, 2)),
|
|
"V0_9_0_RC1" => Ok(KrillVersion::candidate(0, 9, 0, 1)),
|
|
"V0_9_0" => Ok(KrillVersion::release(0, 9, 0)),
|
|
_ => Err(KrillVersionParseError::for_str(s)),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for KrillVersion {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(
|
|
f,
|
|
"{}.{}.{}{}",
|
|
self.major, self.minor, self.patch, self.release_type
|
|
)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct KrillVersionParseError(String);
|
|
|
|
impl KrillVersionParseError {
|
|
fn for_str(s: &str) -> Self {
|
|
KrillVersionParseError(s.to_string())
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for KrillVersionParseError {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "Could not parse Krill version from string: {}", self.0)
|
|
}
|
|
}
|
|
|
|
impl Ord for KrillVersion {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
let mut res = self.major.cmp(&other.major);
|
|
|
|
// use res.is_eq() when the minimum rust requirement will be 1.53 or
|
|
// higher
|
|
if res == Ordering::Equal {
|
|
res = self.minor.cmp(&other.minor);
|
|
}
|
|
|
|
if res == Ordering::Equal {
|
|
res = self.patch.cmp(&other.patch);
|
|
}
|
|
|
|
if res == Ordering::Equal {
|
|
res = self.release_type.cmp(&other.release_type);
|
|
}
|
|
|
|
res
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for KrillVersion {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Serialize for KrillVersion {
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
self.to_string().serialize(serializer)
|
|
}
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for KrillVersion {
|
|
fn deserialize<D>(
|
|
deserializer: D,
|
|
) -> std::result::Result<KrillVersion, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
let string = String::deserialize(deserializer)?;
|
|
KrillVersion::from_str(string.as_str()).map_err(de::Error::custom)
|
|
}
|
|
}
|
|
|
|
|
|
// *Warning:* This type is used in stored state.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
enum KrillVersionReleaseType {
|
|
Release,
|
|
Candidate(u64),
|
|
Dev(String),
|
|
}
|
|
|
|
impl Ord for KrillVersionReleaseType {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
match &self {
|
|
KrillVersionReleaseType::Release => match other {
|
|
KrillVersionReleaseType::Release => Ordering::Equal,
|
|
_ => Ordering::Greater,
|
|
},
|
|
KrillVersionReleaseType::Candidate(nr) => match other {
|
|
KrillVersionReleaseType::Release => Ordering::Less,
|
|
KrillVersionReleaseType::Candidate(nr_other) => {
|
|
nr.cmp(nr_other)
|
|
}
|
|
&KrillVersionReleaseType::Dev(_) => Ordering::Greater,
|
|
},
|
|
KrillVersionReleaseType::Dev(_) => match other {
|
|
KrillVersionReleaseType::Dev(_) => Ordering::Equal,
|
|
_ => Ordering::Less,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for KrillVersionReleaseType {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for KrillVersionReleaseType {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match self {
|
|
KrillVersionReleaseType::Release => write!(f, ""),
|
|
KrillVersionReleaseType::Candidate(nr) => write!(f, "-rc{}", nr),
|
|
KrillVersionReleaseType::Dev(text) => write!(f, "-{}", text),
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//------------ Tests ---------------------------------------------------------
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn krill_version_from_current_cargo_version() {
|
|
KrillVersion::code_version();
|
|
}
|
|
|
|
#[test]
|
|
fn krill_version_pre_0_9_1_enums() {
|
|
KrillVersion::from_str("V0_6").unwrap();
|
|
KrillVersion::from_str("V0_7").unwrap();
|
|
KrillVersion::from_str("V0_8_0_RC1").unwrap();
|
|
KrillVersion::from_str("V0_8").unwrap();
|
|
KrillVersion::from_str("V0_8_1_RC1").unwrap();
|
|
KrillVersion::from_str("V0_8_1").unwrap();
|
|
KrillVersion::from_str("V0_8_2").unwrap();
|
|
KrillVersion::from_str("V0_9_0_RC1").unwrap();
|
|
KrillVersion::from_str("V0_9_0").unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn krill_version_from_str() {
|
|
KrillVersion::from_str("0.9.1").unwrap();
|
|
KrillVersion::from_str("0.9.1-rc1").unwrap();
|
|
KrillVersion::from_str("0.9.1-bis").unwrap();
|
|
|
|
// We do not support short, or random notations including but not
|
|
// limited to:
|
|
assert!(KrillVersion::from_str("v0.9.1").is_err());
|
|
assert!(KrillVersion::from_str("0.9-bis").is_err());
|
|
assert!(KrillVersion::from_str("some garbage").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn krill_version_ordering() {
|
|
let v0_9_1 = KrillVersion::from_str("0.9.1").unwrap();
|
|
let v0_9_1_rc1 = KrillVersion::from_str("0.9.1-rc1").unwrap();
|
|
let v0_9_1_rc2 = KrillVersion::from_str("0.9.1-rc2").unwrap();
|
|
let v0_9_1_dev = KrillVersion::from_str("0.9.1-dev").unwrap();
|
|
|
|
assert!(v0_9_1 > v0_9_1_rc1);
|
|
assert!(v0_9_1_rc2 > v0_9_1_rc1);
|
|
assert!(v0_9_1_rc1 > v0_9_1_dev);
|
|
|
|
let v0_9_0 = KrillVersion::from_str("0.9.0").unwrap();
|
|
assert!(v0_9_1 > v0_9_0);
|
|
assert!(v0_9_1_rc2 > v0_9_0);
|
|
assert!(v0_9_1_dev > v0_9_0);
|
|
}
|
|
}
|
|
|