mol_core/
semantic.rs

1use std::str::FromStr;
2
3use crate::error::{VersionBumpError, VersionParseError};
4use crate::version::{VersionEditor, Versioned};
5
6#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
7enum SemanticVersion {
8  Patch,
9  Minor,
10  Major,
11}
12
13#[derive(Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
14pub struct Semantic {
15  r#type: SemanticVersion,
16}
17
18impl Semantic {
19  pub fn minor() -> Self {
20    Semantic {
21      r#type: SemanticVersion::Minor,
22    }
23  }
24  pub fn major() -> Self {
25    Semantic {
26      r#type: SemanticVersion::Major,
27    }
28  }
29  pub fn patch() -> Self {
30    Semantic {
31      r#type: SemanticVersion::Patch,
32    }
33  }
34}
35
36impl Versioned for Semantic {
37  fn apply(&self, current: &str) -> Result<String, VersionBumpError> {
38    let mut current = current.split('.');
39
40    let major = current
41      .next()
42      .map(|val| val.parse::<i32>().ok())
43      .flatten()
44      .ok_or(VersionBumpError)?;
45    let minor = current
46      .next()
47      .map(|val| val.parse::<i32>().ok())
48      .flatten()
49      .ok_or(VersionBumpError)?;
50    // TODO: Allow dev builds and not
51    let patch = current
52      .next()
53      .map(|val| val.parse::<i32>().ok())
54      .flatten()
55      .ok_or(VersionBumpError)?;
56
57    Ok(match self.r#type {
58      SemanticVersion::Major => format!("{}.{}.{}", major + 1, 0, 0),
59      SemanticVersion::Minor => format!("{}.{}.{}", major, minor + 1, 0),
60      SemanticVersion::Patch => format!("{}.{}.{}", major, minor, patch + 1),
61    })
62  }
63}
64
65impl VersionEditor for Semantic {
66  // TODO: add mask validation
67  fn mask<'a>(mask: &str, version: &'a str) -> &'a str {
68    &version[..mask.len()]
69  }
70
71  fn r#match(mask: &str, version: &str) -> bool {
72    Self::mask(mask, version) == mask
73  }
74
75  fn options() -> Vec<Self> {
76    vec![Self::patch(), Self::minor(), Self::major()]
77  }
78}
79
80impl Default for Semantic {
81  fn default() -> Self {
82    Semantic::patch()
83  }
84}
85
86impl From<SemanticVersion> for Semantic {
87  fn from(r#type: SemanticVersion) -> Self {
88    Semantic { r#type }
89  }
90}
91
92impl FromStr for Semantic {
93  type Err = VersionParseError;
94  fn from_str(value: &str) -> Result<Self, Self::Err> {
95    match value.to_lowercase().as_str() {
96      "patch" => Ok(Semantic::patch()),
97      "minor" => Ok(Semantic::minor()),
98      "major" => Ok(Semantic::major()),
99      _ => Err(VersionParseError::from(value.to_string())),
100    }
101  }
102}
103
104impl ToString for Semantic {
105  fn to_string(&self) -> String {
106    match self.r#type {
107      SemanticVersion::Patch => "patch",
108      SemanticVersion::Minor => "minor",
109      SemanticVersion::Major => "major",
110    }
111    .to_owned()
112  }
113}
114
115#[cfg(test)]
116mod tests {
117
118  use super::*;
119  use crate::version::{VersionMod, Versioned};
120
121  #[test]
122  fn from_str() {
123    let strings = vec!["patch", "minor", "minor", "major"];
124
125    let versions: Vec<VersionMod<Semantic>> = strings
126      .iter()
127      .filter_map(|item| VersionMod::from_str(item).ok())
128      .collect();
129
130    assert_eq!(
131      versions,
132      vec![
133        VersionMod::new(Semantic::patch()),
134        VersionMod::new(Semantic::minor()),
135        VersionMod::new(Semantic::minor()),
136        VersionMod::new(Semantic::major()),
137      ]
138    );
139  }
140
141  #[test]
142  fn to_str() {
143    let versions = vec![
144      VersionMod::new(Semantic::patch()),
145      VersionMod::new(Semantic::minor()),
146      VersionMod::new(Semantic::minor()),
147      VersionMod::new(Semantic::major()),
148    ];
149
150    let strings: Vec<String> = versions.iter().map(|item| item.to_string()).collect();
151
152    assert_eq!(strings, vec!["patch", "minor", "minor", "major"]);
153  }
154
155  // TODO: this
156  // #[test]
157  // fn canary_apply() {
158  //   let version = VersionMod::new(Semantic::major());
159
160  //   let bumped = version.apply("0.0.1-alpha.0");
161
162  //   assert!(bumped.is_ok());
163
164  //   assert_eq!(bumped.unwrap(), "0.0.2".to_owned())
165  // }
166
167  #[test]
168  fn major_apply() {
169    let version = VersionMod::new(Semantic::major());
170
171    let bumped = version.apply("0.4.1");
172
173    assert!(bumped.is_ok());
174
175    assert_eq!(bumped.unwrap(), "1.0.0".to_owned())
176  }
177
178  #[test]
179  fn minor_apply() {
180    let version = VersionMod::new(Semantic::minor());
181
182    let bumped = version.apply("4.1.1");
183
184    assert!(bumped.is_ok());
185
186    assert_eq!(bumped.unwrap(), "4.2.0".to_owned())
187  }
188
189  #[test]
190  fn patch_apply() {
191    let version = VersionMod::new(Semantic::patch());
192
193    let bumped = version.apply("0.4.1");
194
195    assert!(bumped.is_ok());
196
197    assert_eq!(bumped.unwrap(), "0.4.2".to_owned())
198  }
199}