casper_types/
semver.rs

1use alloc::vec::Vec;
2use core::{
3    convert::TryFrom,
4    fmt::{self, Display, Formatter},
5    num::ParseIntError,
6};
7
8#[cfg(feature = "datasize")]
9use datasize::DataSize;
10use serde::{Deserialize, Serialize};
11
12use crate::bytesrepr::{self, Error, FromBytes, ToBytes, U32_SERIALIZED_LENGTH};
13
14/// Length of SemVer when serialized
15pub const SEM_VER_SERIALIZED_LENGTH: usize = 3 * U32_SERIALIZED_LENGTH;
16
17/// A struct for semantic versioning.
18#[derive(
19    Copy, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize,
20)]
21#[cfg_attr(feature = "datasize", derive(DataSize))]
22pub struct SemVer {
23    /// Major version.
24    pub major: u32,
25    /// Minor version.
26    pub minor: u32,
27    /// Patch version.
28    pub patch: u32,
29}
30
31impl SemVer {
32    /// Version 1.0.0.
33    pub const V1_0_0: SemVer = SemVer {
34        major: 1,
35        minor: 0,
36        patch: 0,
37    };
38
39    /// Constructs a new `SemVer` from the given semver parts.
40    pub const fn new(major: u32, minor: u32, patch: u32) -> SemVer {
41        SemVer {
42            major,
43            minor,
44            patch,
45        }
46    }
47}
48
49impl ToBytes for SemVer {
50    fn to_bytes(&self) -> Result<Vec<u8>, Error> {
51        let mut ret = bytesrepr::unchecked_allocate_buffer(self);
52        ret.append(&mut self.major.to_bytes()?);
53        ret.append(&mut self.minor.to_bytes()?);
54        ret.append(&mut self.patch.to_bytes()?);
55        Ok(ret)
56    }
57
58    fn serialized_length(&self) -> usize {
59        SEM_VER_SERIALIZED_LENGTH
60    }
61}
62
63impl FromBytes for SemVer {
64    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
65        let (major, rem): (u32, &[u8]) = FromBytes::from_bytes(bytes)?;
66        let (minor, rem): (u32, &[u8]) = FromBytes::from_bytes(rem)?;
67        let (patch, rem): (u32, &[u8]) = FromBytes::from_bytes(rem)?;
68        Ok((SemVer::new(major, minor, patch), rem))
69    }
70}
71
72impl Display for SemVer {
73    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
74        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
75    }
76}
77
78/// Parsing error when creating a SemVer.
79#[derive(Debug, Clone, PartialEq, Eq)]
80pub enum ParseSemVerError {
81    /// Invalid version format.
82    InvalidVersionFormat,
83    /// Error parsing an integer.
84    ParseIntError(ParseIntError),
85}
86
87impl Display for ParseSemVerError {
88    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
89        match self {
90            ParseSemVerError::InvalidVersionFormat => formatter.write_str("invalid version format"),
91            ParseSemVerError::ParseIntError(error) => error.fmt(formatter),
92        }
93    }
94}
95
96impl From<ParseIntError> for ParseSemVerError {
97    fn from(error: ParseIntError) -> ParseSemVerError {
98        ParseSemVerError::ParseIntError(error)
99    }
100}
101
102impl TryFrom<&str> for SemVer {
103    type Error = ParseSemVerError;
104    fn try_from(value: &str) -> Result<SemVer, Self::Error> {
105        let tokens: Vec<&str> = value.split('.').collect();
106        if tokens.len() != 3 {
107            return Err(ParseSemVerError::InvalidVersionFormat);
108        }
109
110        Ok(SemVer {
111            major: tokens[0].parse()?,
112            minor: tokens[1].parse()?,
113            patch: tokens[2].parse()?,
114        })
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use core::convert::TryInto;
122
123    #[test]
124    fn should_compare_semver_versions() {
125        assert!(SemVer::new(0, 0, 0) < SemVer::new(1, 2, 3));
126        assert!(SemVer::new(1, 1, 0) < SemVer::new(1, 2, 0));
127        assert!(SemVer::new(1, 0, 0) < SemVer::new(1, 2, 0));
128        assert!(SemVer::new(1, 0, 0) < SemVer::new(1, 2, 3));
129        assert!(SemVer::new(1, 2, 0) < SemVer::new(1, 2, 3));
130        assert!(SemVer::new(1, 2, 3) == SemVer::new(1, 2, 3));
131        assert!(SemVer::new(1, 2, 3) >= SemVer::new(1, 2, 3));
132        assert!(SemVer::new(1, 2, 3) <= SemVer::new(1, 2, 3));
133        assert!(SemVer::new(2, 0, 0) >= SemVer::new(1, 99, 99));
134        assert!(SemVer::new(2, 0, 0) > SemVer::new(1, 99, 99));
135    }
136
137    #[test]
138    fn parse_from_string() {
139        let ver1: SemVer = "100.20.3".try_into().expect("should parse");
140        assert_eq!(ver1, SemVer::new(100, 20, 3));
141        let ver2: SemVer = "0.0.1".try_into().expect("should parse");
142        assert_eq!(ver2, SemVer::new(0, 0, 1));
143
144        assert!(SemVer::try_from("1.a.2.3").is_err());
145        assert!(SemVer::try_from("1. 2.3").is_err());
146        assert!(SemVer::try_from("12345124361461.0.1").is_err());
147        assert!(SemVer::try_from("1.2.3.4").is_err());
148        assert!(SemVer::try_from("1.2").is_err());
149        assert!(SemVer::try_from("1").is_err());
150        assert!(SemVer::try_from("0").is_err());
151    }
152}