miden_project/dependencies/
version.rs1use core::{borrow::Borrow, fmt, str::FromStr};
2
3pub use miden_assembly_syntax::semver::{Error as SemVerError, Version as SemVer, VersionReq};
4use miden_core::{LexicographicWord, Word};
5
6use super::VersionRequirement;
7
8#[derive(Debug, thiserror::Error)]
10pub enum InvalidVersionError {
11 #[error("invalid digest: {0}")]
12 Digest(&'static str),
13 #[error("invalid semantic version: {0}")]
14 Version(SemVerError),
15}
16
17#[derive(Debug, Clone)]
34pub struct Version {
35 pub version: SemVer,
39 pub digest: Option<LexicographicWord>,
44}
45
46impl Version {
47 pub fn new(version: SemVer, digest: Word) -> Self {
49 Self { version, digest: Some(digest.into()) }
50 }
51
52 pub fn satisfies(&self, requirement: &VersionRequirement) -> bool {
57 match requirement {
58 VersionRequirement::Semantic(req) => req.matches(&self.version),
59 VersionRequirement::Digest(req) => self
60 .digest
61 .as_ref()
62 .is_some_and(|digest| &LexicographicWord::new(req.into_inner()) == digest),
63 }
64 }
65}
66
67impl FromStr for Version {
68 type Err = InvalidVersionError;
69 fn from_str(s: &str) -> Result<Self, Self::Err> {
70 match s.split_once('@') {
71 Some((v, digest)) => {
72 let v = v.parse::<SemVer>().map_err(InvalidVersionError::Version)?;
73 let digest = Word::parse(digest).map_err(InvalidVersionError::Digest)?;
74 Ok(Self::new(v, digest))
75 },
76 None => {
77 let v = s.parse::<SemVer>().map_err(InvalidVersionError::Version)?;
78 Ok(Self::from(v))
79 },
80 }
81 }
82}
83
84impl From<SemVer> for Version {
85 fn from(version: SemVer) -> Self {
86 Self { version, digest: None }
87 }
88}
89
90impl From<(SemVer, Word)> for Version {
91 fn from(version: (SemVer, Word)) -> Self {
92 let (version, word) = version;
93 Self { version, digest: Some(word.into()) }
94 }
95}
96
97impl Borrow<SemVer> for Version {
98 #[inline(always)]
99 fn borrow(&self) -> &SemVer {
100 &self.version
101 }
102}
103
104impl fmt::Display for Version {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 if let Some(digest) = self.digest.as_ref() {
107 write!(f, "{}@{}", &self.version, digest.inner())
108 } else {
109 fmt::Display::fmt(&self.version, f)
110 }
111 }
112}
113
114impl Eq for Version {}
115impl PartialEq for Version {
116 fn eq(&self, other: &Self) -> bool {
117 if self.version != other.version {
118 return false;
119 }
120 if let Some(l) = self.digest.as_ref()
121 && let Some(r) = other.digest.as_ref()
122 {
123 l == r
124 } else {
125 true
126 }
127 }
128}
129
130impl PartialOrd for Version {
131 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
132 Some(self.cmp(other))
133 }
134}
135
136impl Ord for Version {
137 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
138 use core::cmp::Ordering;
139 self.version.cmp_precedence(&other.version).then_with(|| {
140 if let Some(l) = self.digest.as_ref()
141 && let Some(r) = other.digest.as_ref()
142 {
143 l.cmp(r)
144 } else {
145 Ordering::Equal
146 }
147 })
148 }
149}