stoolap/common/
version.rs1pub const VERSION: &str = env!("CARGO_PKG_VERSION");
22
23pub const MAJOR: u32 = parse_version_component(env!("CARGO_PKG_VERSION_MAJOR"));
25
26pub const MINOR: u32 = parse_version_component(env!("CARGO_PKG_VERSION_MINOR"));
28
29pub const PATCH: u32 = parse_version_component(env!("CARGO_PKG_VERSION_PATCH"));
31
32const fn parse_version_component(s: &str) -> u32 {
34 let bytes = s.as_bytes();
35 let mut result: u32 = 0;
36 let mut i = 0;
37 while i < bytes.len() {
38 let digit = bytes[i] - b'0';
39 result = result * 10 + digit as u32;
40 i += 1;
41 }
42 result
43}
44
45pub const GIT_COMMIT: &str = match option_env!("STOOLAP_GIT_COMMIT") {
48 Some(commit) => commit,
49 None => "unknown",
50};
51
52pub const BUILD_TIME: &str = match option_env!("STOOLAP_BUILD_TIME") {
55 Some(time) => time,
56 None => "unknown",
57};
58
59pub fn version() -> &'static str {
61 VERSION
62}
63
64pub fn version_info() -> String {
66 format!(
67 "stoolap {} (commit: {}, built: {})",
68 VERSION, GIT_COMMIT, BUILD_TIME
69 )
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub struct SemVer {
75 pub major: u32,
76 pub minor: u32,
77 pub patch: u32,
78}
79
80impl SemVer {
81 pub const fn new(major: u32, minor: u32, patch: u32) -> Self {
83 Self {
84 major,
85 minor,
86 patch,
87 }
88 }
89
90 pub const fn current() -> Self {
92 Self::new(MAJOR, MINOR, PATCH)
93 }
94
95 pub fn is_compatible_with(&self, other: &SemVer) -> bool {
98 self.major == other.major && self.minor >= other.minor
99 }
100}
101
102impl std::fmt::Display for SemVer {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
105 }
106}
107
108impl std::str::FromStr for SemVer {
109 type Err = String;
110
111 fn from_str(s: &str) -> Result<Self, Self::Err> {
112 let parts: Vec<&str> = s.split('.').collect();
113 if parts.len() != 3 {
114 return Err(format!("invalid version format: {}", s));
115 }
116
117 let major = parts[0]
118 .parse()
119 .map_err(|_| format!("invalid major version: {}", parts[0]))?;
120 let minor = parts[1]
121 .parse()
122 .map_err(|_| format!("invalid minor version: {}", parts[1]))?;
123 let patch = parts[2]
124 .parse()
125 .map_err(|_| format!("invalid patch version: {}", parts[2]))?;
126
127 Ok(SemVer::new(major, minor, patch))
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_version_string() {
137 let expected = format!("{}.{}.{}", MAJOR, MINOR, PATCH);
139 assert_eq!(version(), expected);
140 }
141
142 #[test]
143 fn test_version_info() {
144 let info = version_info();
145 assert!(info.contains("stoolap"));
146 assert!(info.contains(version()));
147 }
148
149 #[test]
150 fn test_git_commit_default() {
151 assert!(!GIT_COMMIT.is_empty());
154 }
155
156 #[test]
157 fn test_build_time_default() {
158 assert!(!BUILD_TIME.is_empty());
160 }
161
162 #[test]
163 fn test_semver_new() {
164 let v = SemVer::new(1, 2, 3);
165 assert_eq!(v.major, 1);
166 assert_eq!(v.minor, 2);
167 assert_eq!(v.patch, 3);
168 }
169
170 #[test]
171 fn test_semver_current() {
172 let v = SemVer::current();
173 assert_eq!(v.major, MAJOR);
174 assert_eq!(v.minor, MINOR);
175 assert_eq!(v.patch, PATCH);
176 }
177
178 #[test]
179 fn test_semver_display() {
180 let v = SemVer::new(1, 2, 3);
181 assert_eq!(v.to_string(), "1.2.3");
182 }
183
184 #[test]
185 fn test_semver_from_str() {
186 let v: SemVer = "1.2.3".parse().unwrap();
187 assert_eq!(v, SemVer::new(1, 2, 3));
188
189 let v: SemVer = version().parse().unwrap();
191 assert_eq!(v, SemVer::current());
192 }
193
194 #[test]
195 fn test_semver_from_str_invalid() {
196 assert!("1.2".parse::<SemVer>().is_err());
197 assert!("1.2.3.4".parse::<SemVer>().is_err());
198 assert!("a.b.c".parse::<SemVer>().is_err());
199 assert!("".parse::<SemVer>().is_err());
200 }
201
202 #[test]
203 fn test_semver_compatibility() {
204 let v1 = SemVer::new(1, 2, 0);
205 let v2 = SemVer::new(1, 1, 0);
206 let v3 = SemVer::new(1, 3, 0);
207 let v4 = SemVer::new(2, 0, 0);
208
209 assert!(v1.is_compatible_with(&v2));
211
212 assert!(!v1.is_compatible_with(&v3));
214
215 assert!(!v1.is_compatible_with(&v4));
217
218 assert!(v1.is_compatible_with(&v1));
220 }
221}