1use crate::data::{DataError, Validate, ValidationError};
4use core::fmt;
5use semver::{Version, VersionReq};
6use serde::{Deserialize, Deserializer, de};
7use serde_json::Value;
8use serde_with::SerializeDisplay;
9use std::str::FromStr;
10
11#[derive(Debug, PartialEq, SerializeDisplay)]
14pub struct MyVersion(Version);
15
16impl MyVersion {
17 pub fn major(&self) -> u64 {
19 self.0.major
20 }
21
22 pub fn minor(&self) -> u64 {
24 self.0.minor
25 }
26
27 pub fn patch(&self) -> u64 {
29 self.0.patch
30 }
31
32 fn is_excluded(&self) -> bool {
34 self.0.major == 1 && self.0.minor == 1
35 }
36}
37
38impl FromStr for MyVersion {
39 type Err = DataError;
40
41 fn from_str(s: &str) -> Result<Self, Self::Err> {
42 let parts: Vec<&str> = s.trim().split('.').collect();
44 let padded = match parts.len() {
45 1 => format!("{}.0.0", parts[0]),
46 2 => format!("{}.{}.0", parts[0], parts[1]),
47 _ => s.to_string(),
48 };
49 let sv = Version::parse(&padded)?;
50 Ok(MyVersion(sv))
51 }
52}
53
54impl From<f64> for MyVersion {
55 fn from(float_value: f64) -> Self {
57 let major = float_value.trunc() as u64;
58 let minor = (float_value.fract() * 1000.0).round() as u64;
59 MyVersion(Version::new(major, minor, 0))
60 }
61}
62
63impl<'de> Deserialize<'de> for MyVersion {
64 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
65 where
66 D: Deserializer<'de>,
67 {
68 let value: Value = Deserialize::deserialize(deserializer)?;
69 match value {
70 Value::String(s) => MyVersion::from_str(&s).map_err(de::Error::custom),
71 Value::Number(num) => {
72 if let Some(z_float) = num.as_f64() {
73 Ok(MyVersion::from(z_float))
74 } else {
75 Err(de::Error::custom("Invalid number format"))
76 }
77 }
78 _ => Err(de::Error::custom("Expected string | number")),
79 }
80 }
81}
82
83impl fmt::Display for MyVersion {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 write!(f, "{}", self.0)
86 }
87}
88
89impl Validate for MyVersion {
90 fn validate(&self) -> Vec<super::ValidationError> {
91 let mut vec = vec![];
92
93 let range = VersionReq::parse(">=1.0.0, <=2.0.0").unwrap();
94 if range.matches(&self.0) && !self.is_excluded() {
95 } else {
97 vec.push(ValidationError::ConstraintViolation(
98 format!("Version '{self}' is invalid or not allowed").into(),
99 ))
100 }
101
102 vec
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use tracing_test::traced_test;
110
111 #[test]
112 fn test_no_patch() {
113 let v = MyVersion::from_str("1.0").unwrap();
114 assert_eq!(v.major(), 1);
115 assert_eq!(v.minor(), 0);
116 assert_eq!(v.patch(), 0);
117
118 let v: MyVersion = serde_json::from_str("1.0").unwrap();
120 assert_eq!(v.major(), 1);
121 assert_eq!(v.minor(), 0);
122 assert_eq!(v.patch(), 0);
123 }
124
125 #[traced_test]
126 #[test]
127 fn test_invalid() {
128 assert!(!MyVersion::from_str("0.9.9").unwrap().is_valid());
129 assert!(!MyVersion::from_str("2.0.1-beta").unwrap().is_valid());
130 assert!(!MyVersion::from_str("1.1.0").unwrap().is_valid());
131 }
132
133 #[traced_test]
134 #[test]
135 fn test_valid() {
136 assert!(MyVersion::from_str("1.0").unwrap().is_valid());
137 assert!(MyVersion::from_str("1.0.3").unwrap().is_valid());
138 assert!(MyVersion::from_str("2.0.0").unwrap().is_valid());
139 }
140
141 #[derive(Debug, serde::Deserialize, serde::Serialize)]
142 struct Foo {
143 ver: Option<MyVersion>,
144 }
145
146 #[traced_test]
147 #[test]
148 fn test_serde() {
149 const F1: &str = r#"{"ver":"1.0"}"#;
150 const F2: &str = r#"{"ver":"1.0.3"}"#;
151 const F3: &str = r#"{"ver":"2.0.0"}"#;
152
153 let f: Foo = serde_json::from_str(F1).unwrap();
154 assert_eq!(f.ver, Some(MyVersion(Version::new(1, 0, 0))));
155
156 let f: Foo = serde_json::from_str(F2).unwrap();
157 assert_eq!(f.ver, Some(MyVersion(Version::new(1, 0, 3))));
158
159 let f: Foo = serde_json::from_str(F3).unwrap();
160 assert_eq!(f.ver, Some(MyVersion(Version::new(2, 0, 0))));
161 }
162}