1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
5pub struct Version {
6 inner: semver::Version,
7}
8
9#[derive(Debug, Clone)]
11pub struct VersionReq {
12 inner: semver::VersionReq,
13}
14
15impl Version {
16 pub fn parse(s: &str) -> Result<Self, String> {
18 semver::Version::parse(s)
19 .map(|v| Version { inner: v })
20 .map_err(|e| format!("Invalid version '{s}': {e}"))
21 }
22
23 pub fn as_semver(&self) -> &semver::Version {
25 &self.inner
26 }
27}
28
29impl fmt::Display for Version {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 write!(f, "{}", self.inner)
32 }
33}
34
35impl VersionReq {
36 pub fn parse(s: &str) -> Result<Self, String> {
38 if let Ok(req) = semver::VersionReq::parse(s) {
40 return Ok(VersionReq { inner: req });
41 }
42 if let Ok(ver) = semver::Version::parse(s) {
44 let req = semver::VersionReq::parse(&format!("^{ver}"))
45 .map_err(|e| format!("Invalid version req '{s}': {e}"))?;
46 return Ok(VersionReq { inner: req });
47 }
48 Err(format!("Invalid version requirement '{s}'"))
49 }
50
51 pub fn matches(&self, version: &Version) -> bool {
53 self.inner.matches(&version.inner)
54 }
55}
56
57impl fmt::Display for VersionReq {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(f, "{}", self.inner)
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn version_parse_and_display() {
69 let v = Version::parse("1.2.3").unwrap();
70 assert_eq!(v.to_string(), "1.2.3");
71
72 assert!(Version::parse("not-a-version").is_err());
73 }
74
75 #[test]
76 fn version_req_matching() {
77 let req = VersionReq::parse("^1.0").unwrap();
78 assert!(req.matches(&Version::parse("1.0.0").unwrap()));
79 assert!(req.matches(&Version::parse("1.9.9").unwrap()));
80 assert!(!req.matches(&Version::parse("2.0.0").unwrap()));
81
82 let req2 = VersionReq::parse(">=2.0, <3.0").unwrap();
83 assert!(req2.matches(&Version::parse("2.5.0").unwrap()));
84 assert!(!req2.matches(&Version::parse("3.0.0").unwrap()));
85 }
86}