mur_common/skill/
constraint.rs1use semver::{Version, VersionReq};
2
3#[derive(Debug, Clone)]
4pub struct Constraint(pub VersionReq);
5
6#[derive(Debug, thiserror::Error)]
7pub enum ConstraintError {
8 #[error("invalid version requirement '{0}': {1}")]
9 Parse(String, String),
10}
11
12impl Constraint {
13 pub fn parse(s: &str) -> Result<Self, ConstraintError> {
17 let trimmed = s.trim();
18 if trimmed.is_empty() || trimmed == "*" {
19 return Ok(Self(VersionReq::STAR));
20 }
21 VersionReq::parse(trimmed)
22 .map(Self)
23 .map_err(|e| ConstraintError::Parse(trimmed.into(), e.to_string()))
24 }
25
26 pub fn matches(&self, v: &Version) -> bool {
27 self.0.matches(v)
28 }
29
30 pub fn any() -> Self {
31 Self(VersionReq::STAR)
32 }
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38
39 #[test]
40 fn star_matches_anything() {
41 let c = Constraint::parse("*").unwrap();
42 assert!(c.matches(&Version::parse("0.0.1").unwrap()));
43 assert!(c.matches(&Version::parse("99.99.99").unwrap()));
44 }
45
46 #[test]
47 fn ge_pins_minimum() {
48 let c = Constraint::parse(">=1.0.0").unwrap();
49 assert!(!c.matches(&Version::parse("0.9.9").unwrap()));
50 assert!(c.matches(&Version::parse("1.0.0").unwrap()));
51 assert!(c.matches(&Version::parse("2.0.0").unwrap()));
52 }
53
54 #[test]
55 fn caret_locks_major() {
56 let c = Constraint::parse("^1.2.3").unwrap();
57 assert!(c.matches(&Version::parse("1.9.0").unwrap()));
58 assert!(!c.matches(&Version::parse("2.0.0").unwrap()));
59 }
60
61 #[test]
62 fn tilde_locks_minor() {
63 let c = Constraint::parse("~1.2.3").unwrap();
64 assert!(c.matches(&Version::parse("1.2.9").unwrap()));
65 assert!(!c.matches(&Version::parse("1.3.0").unwrap()));
66 }
67
68 #[test]
69 fn exact_pins_exact() {
70 let c = Constraint::parse("=1.2.3").unwrap();
71 assert!(c.matches(&Version::parse("1.2.3").unwrap()));
72 assert!(!c.matches(&Version::parse("1.2.4").unwrap()));
73 }
74
75 #[test]
76 fn empty_means_any() {
77 let c = Constraint::parse("").unwrap();
78 assert!(c.matches(&Version::parse("0.0.1").unwrap()));
79 }
80
81 #[test]
82 fn garbage_fails() {
83 assert!(Constraint::parse("not-a-version").is_err());
84 }
85}