rustev_contract/
version.rs1use std::{cmp::Ordering, fmt, str::FromStr};
3
4#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub struct Version([String; 3]);
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum VersionError {
10 InvalidVersion,
11 InvalidRange,
12 ComparatorCount,
13}
14impl fmt::Display for VersionError {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 f.write_str(match self {
17 Self::InvalidVersion => "expected MAJOR.MINOR.PATCH without leading zeros or suffixes",
18 Self::InvalidRange => "expected comparators separated by single spaces",
19 Self::ComparatorCount => "expected one to four comparators",
20 })
21 }
22}
23impl std::error::Error for VersionError {}
24impl FromStr for Version {
25 type Err = VersionError;
26 fn from_str(s: &str) -> Result<Self, Self::Err> {
27 let parts: Vec<_> = s.split('.').collect();
28 if parts.len() != 3
29 || parts.iter().any(|p| {
30 p.is_empty()
31 || !p.bytes().all(|b| b.is_ascii_digit())
32 || (p.len() > 1 && p.starts_with('0'))
33 })
34 {
35 return Err(VersionError::InvalidVersion);
36 }
37 Ok(Self([parts[0].into(), parts[1].into(), parts[2].into()]))
38 }
39}
40impl Ord for Version {
41 fn cmp(&self, other: &Self) -> Ordering {
42 self.0
43 .iter()
44 .zip(&other.0)
45 .map(|(a, b)| a.len().cmp(&b.len()).then_with(|| a.cmp(b)))
46 .find(|c| *c != Ordering::Equal)
47 .unwrap_or(Ordering::Equal)
48 }
49}
50impl PartialOrd for Version {
51 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
52 Some(self.cmp(other))
53 }
54}
55impl fmt::Display for Version {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 write!(f, "{}.{}.{}", self.0[0], self.0[1], self.0[2])
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct VersionRange {
64 text: String,
65 comparators: Vec<(Comparison, Version)>,
66}
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68enum Comparison {
69 Ge,
70 Gt,
71 Le,
72 Lt,
73 Eq,
74}
75impl FromStr for VersionRange {
76 type Err = VersionError;
77 fn from_str(s: &str) -> Result<Self, Self::Err> {
78 let parts: Vec<_> = s.split(' ').collect();
79 if parts.iter().any(|p| p.is_empty()) {
80 return Err(VersionError::InvalidRange);
81 }
82 if parts.len() > 4 {
83 return Err(VersionError::ComparatorCount);
84 }
85 let mut comparators = Vec::new();
86 for p in parts {
87 let (op, v) = [
88 (">=", Comparison::Ge),
89 ("<=", Comparison::Le),
90 (">", Comparison::Gt),
91 ("<", Comparison::Lt),
92 ("=", Comparison::Eq),
93 ]
94 .into_iter()
95 .find_map(|(prefix, op)| p.strip_prefix(prefix).map(|v| (op, v)))
96 .unwrap_or((Comparison::Eq, p));
97 comparators.push((op, v.parse()?));
98 }
99 Ok(Self {
100 text: s.into(),
101 comparators,
102 })
103 }
104}
105impl VersionRange {
106 pub fn satisfies(&self, version: &Version) -> bool {
107 self.comparators.iter().all(|(op, v)| match op {
108 Comparison::Ge => version >= v,
109 Comparison::Gt => version > v,
110 Comparison::Le => version <= v,
111 Comparison::Lt => version < v,
112 Comparison::Eq => version == v,
113 })
114 }
115}
116impl fmt::Display for VersionRange {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 f.write_str(&self.text)
119 }
120}