diskann_record/
version.rs1#[cfg(feature = "serde")]
9use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct Version {
29 pub major: u32,
30 pub minor: u32,
31}
32
33impl Version {
34 pub const fn new(major: u32, minor: u32) -> Self {
36 Self { major, minor }
37 }
38}
39
40impl std::fmt::Display for Version {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 write!(f, "{}.{}", self.major, self.minor)
43 }
44}
45
46impl std::str::FromStr for Version {
47 type Err = ParseVersionError;
48
49 fn from_str(s: &str) -> Result<Self, Self::Err> {
50 let mut parts = s.split('.');
51 let major = parts.next().and_then(|s| s.parse::<u32>().ok());
52 let minor = parts.next().and_then(|s| s.parse::<u32>().ok());
53 match (major, minor, parts.next()) {
54 (Some(major), Some(minor), None) => Ok(Version { major, minor }),
55 _ => Err(ParseVersionError(s.to_owned())),
56 }
57 }
58}
59
60#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct ParseVersionError(String);
63
64impl std::fmt::Display for ParseVersionError {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(
67 f,
68 "unknown version {:?}: expected two `.`-separated u32 components",
69 self.0,
70 )
71 }
72}
73
74impl std::error::Error for ParseVersionError {}
75
76#[cfg(feature = "serde")]
77impl Serialize for Version {
78 fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
79 ser.collect_str(self)
80 }
81}
82
83#[cfg(feature = "serde")]
84impl<'de> Deserialize<'de> for Version {
85 fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
86 struct VersionVisitor;
87
88 impl de::Visitor<'_> for VersionVisitor {
89 type Value = Version;
90
91 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
92 f.write_str("a version string of the form \"major.minor\"")
93 }
94
95 fn visit_str<E: de::Error>(self, v: &str) -> Result<Version, E> {
96 v.parse().map_err(E::custom)
97 }
98 }
99
100 de.deserialize_str(VersionVisitor)
101 }
102}
103
104#[cfg(all(test, feature = "disk"))]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn serializes_as_dotted_string() {
110 let json = serde_json::to_string(&Version::new(1, 2)).unwrap();
111 assert_eq!(json, "\"1.2\"");
112 }
113
114 #[test]
115 fn round_trips_through_json() {
116 let v = Version::new(4, 5);
117 let back: Version = serde_json::from_str(&serde_json::to_string(&v).unwrap()).unwrap();
118 assert_eq!(v, back);
119 }
120
121 #[test]
122 fn rejects_malformed_strings() {
123 for bad in ["\"1\"", "\"1.2.3\"", "\"1.x\"", "\"abc\""] {
124 serde_json::from_str::<Version>(bad)
125 .expect_err("malformed version string must be rejected");
126 }
127 }
128}