miden_assembly/library/
version.rs

1use core::{
2    fmt,
3    str::{self, FromStr},
4};
5
6use crate::{
7    diagnostics::Diagnostic, ByteReader, ByteWriter, Deserializable, DeserializationError,
8    Serializable,
9};
10
11/// Represents a _Semantic Versioning_ version string, without pre-releases.
12#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
13pub struct Version {
14    /// The major version, incremented when breaking changes occur.
15    pub major: u16,
16    /// The minor version, incremented when new features or functionality is introduced.
17    pub minor: u16,
18    /// The patch version, incremented when non-breaking changes are made.
19    pub patch: u16,
20}
21
22/// Construction
23impl Version {
24    /// Returns the current minimal version supported by the code in this crate.
25    #[inline(always)]
26    pub const fn min() -> Self {
27        Self { major: 0, minor: 1, patch: 0 }
28    }
29}
30
31/// Arithmetic
32impl Version {
33    /// Returns a new [Version] clamped to the major version
34    ///
35    /// This is useful for comparing two versions at major version granularity
36    pub const fn to_nearest_major(self) -> Self {
37        Self { minor: 0, patch: 0, ..self }
38    }
39
40    /// Returns a new [Version] clamped to the minor version
41    ///
42    /// This is useful for comparing two versions at minor version granularity
43    pub const fn to_nearest_minor(self) -> Self {
44        Self { patch: 0, ..self }
45    }
46
47    /// Return a new [Version] representing the next major version release
48    pub const fn next_major(self) -> Self {
49        Self {
50            major: self.major + 1,
51            minor: 0,
52            patch: 0,
53        }
54    }
55
56    /// Return a new [Version] representing the next minor version release
57    pub const fn next_minor(self) -> Self {
58        Self { minor: self.minor + 1, patch: 0, ..self }
59    }
60
61    /// Return a new [Version] representing the next patch release
62    pub const fn next_patch(self) -> Self {
63        Self { patch: self.patch + 1, ..self }
64    }
65}
66
67impl Default for Version {
68    fn default() -> Self {
69        Self::min()
70    }
71}
72impl fmt::Display for Version {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
75    }
76}
77impl Serializable for Version {
78    fn write_into<W: ByteWriter>(&self, target: &mut W) {
79        target.write_u16(self.major);
80        target.write_u16(self.minor);
81        target.write_u16(self.patch);
82    }
83}
84impl Deserializable for Version {
85    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
86        let major = source.read_u16()?;
87        let minor = source.read_u16()?;
88        let patch = source.read_u16()?;
89        Ok(Self { major, minor, patch })
90    }
91}
92
93/// Represents errors that occur when parsing a [Version]
94#[derive(Debug, thiserror::Error, Diagnostic)]
95pub enum VersionError {
96    #[error("invalid version string: cannot be empty")]
97    #[diagnostic()]
98    Empty,
99    #[error("invalid version string: missing minor component, expected MAJOR.MINOR.PATCH")]
100    #[diagnostic()]
101    MissingMinor,
102    #[error("invalid version string: missing patch component, expected MAJOR.MINOR.PATCH")]
103    #[diagnostic()]
104    MissingPatch,
105    #[error("invalid version string: could not parse major version: {0}")]
106    #[diagnostic()]
107    Major(core::num::ParseIntError),
108    #[error("invalid version string: could not parse minor version: {0}")]
109    #[diagnostic()]
110    Minor(core::num::ParseIntError),
111    #[error("invalid version string: could not parse patch version: {0}")]
112    #[diagnostic()]
113    Patch(core::num::ParseIntError),
114    #[error(
115        "invalid version string: unsupported pre-release version, \
116        only MAJOR.MINOR.PATCH components are allowed"
117    )]
118    #[diagnostic()]
119    Unsupported,
120}
121
122impl FromStr for Version {
123    type Err = VersionError;
124
125    fn from_str(value: &str) -> Result<Self, Self::Err> {
126        let mut components = value.split('.');
127
128        let major = components
129            .next()
130            .ok_or(VersionError::Empty)?
131            .parse::<u16>()
132            .map_err(VersionError::Major)?;
133        let minor = components
134            .next()
135            .ok_or(VersionError::MissingMinor)?
136            .parse::<u16>()
137            .map_err(VersionError::Minor)?;
138        let patch = components
139            .next()
140            .ok_or(VersionError::MissingPatch)?
141            .parse::<u16>()
142            .map_err(VersionError::Patch)?;
143
144        if components.next().is_some() {
145            Err(VersionError::Unsupported)
146        } else {
147            Ok(Self { major, minor, patch })
148        }
149    }
150}