allsource_core/domain/value_objects/
version.rs1use crate::error::Result;
2use serde::{Deserialize, Serialize};
3use std::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
20pub struct Version(u64);
21
22impl Version {
23 pub const FIRST: Version = Version(1);
25
26 pub fn new(value: u64) -> Result<Self> {
39 Self::validate(value)?;
40 Ok(Self(value))
41 }
42
43 pub(crate) fn new_unchecked(value: u64) -> Self {
48 Self(value)
49 }
50
51 pub fn first() -> Self {
62 Self::FIRST
63 }
64
65 pub fn as_u64(&self) -> u64 {
67 self.0
68 }
69
70 pub fn as_i64(&self) -> i64 {
72 self.0 as i64
73 }
74
75 pub fn as_u32(&self) -> u32 {
77 self.0 as u32
78 }
79
80 pub fn is_first(&self) -> bool {
93 self.0 == 1
94 }
95
96 pub fn next(&self) -> Self {
108 Self(self.0.saturating_add(1))
109 }
110
111 pub fn previous(&self) -> Option<Self> {
128 if self.0 > 1 {
129 Some(Self(self.0 - 1))
130 } else {
131 None
132 }
133 }
134
135 pub fn is_immediately_after(&self, other: &Version) -> bool {
149 self.0 == other.0 + 1
150 }
151
152 fn validate(value: u64) -> Result<()> {
154 if value == 0 {
155 return Err(crate::error::AllSourceError::InvalidInput(
156 "Version must be >= 1".to_string(),
157 ));
158 }
159 Ok(())
160 }
161}
162
163impl fmt::Display for Version {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 write!(f, "{}", self.0)
166 }
167}
168
169impl TryFrom<u64> for Version {
170 type Error = crate::error::AllSourceError;
171
172 fn try_from(value: u64) -> Result<Self> {
173 Version::new(value)
174 }
175}
176
177impl TryFrom<i64> for Version {
178 type Error = crate::error::AllSourceError;
179
180 fn try_from(value: i64) -> Result<Self> {
181 if value < 1 {
182 return Err(crate::error::AllSourceError::InvalidInput(
183 "Version must be >= 1".to_string(),
184 ));
185 }
186 Version::new(value as u64)
187 }
188}
189
190impl TryFrom<u32> for Version {
191 type Error = crate::error::AllSourceError;
192
193 fn try_from(value: u32) -> Result<Self> {
194 Version::new(value as u64)
195 }
196}
197
198impl From<Version> for u64 {
199 fn from(version: Version) -> Self {
200 version.0
201 }
202}
203
204impl From<Version> for i64 {
205 fn from(version: Version) -> Self {
206 version.0 as i64
207 }
208}
209
210impl From<Version> for u32 {
211 fn from(version: Version) -> Self {
212 version.0 as u32
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn test_create_valid_version() {
222 let version = Version::new(1);
223 assert!(version.is_ok());
224 assert_eq!(version.unwrap().as_u64(), 1);
225
226 let version = Version::new(100);
227 assert!(version.is_ok());
228 assert_eq!(version.unwrap().as_u64(), 100);
229 }
230
231 #[test]
232 fn test_reject_zero_version() {
233 let result = Version::new(0);
234 assert!(result.is_err());
235
236 if let Err(e) = result {
237 assert!(e.to_string().contains("must be >= 1"));
238 }
239 }
240
241 #[test]
242 fn test_first_version() {
243 let version = Version::first();
244 assert_eq!(version.as_u64(), 1);
245 assert!(version.is_first());
246 }
247
248 #[test]
249 fn test_is_first() {
250 let v1 = Version::first();
251 let v2 = Version::new(2).unwrap();
252
253 assert!(v1.is_first());
254 assert!(!v2.is_first());
255 }
256
257 #[test]
258 fn test_next_version() {
259 let v1 = Version::first();
260 let v2 = v1.next();
261 let v3 = v2.next();
262
263 assert_eq!(v2.as_u64(), 2);
264 assert_eq!(v3.as_u64(), 3);
265 }
266
267 #[test]
268 fn test_previous_version() {
269 let v1 = Version::first();
270 let v2 = Version::new(2).unwrap();
271 let v3 = Version::new(3).unwrap();
272
273 assert_eq!(v1.previous(), None);
274 assert_eq!(v2.previous(), Some(Version::first()));
275 assert_eq!(v3.previous(), Some(Version::new(2).unwrap()));
276 }
277
278 #[test]
279 fn test_is_immediately_after() {
280 let v1 = Version::first();
281 let v2 = Version::new(2).unwrap();
282 let v3 = Version::new(3).unwrap();
283
284 assert!(v2.is_immediately_after(&v1));
285 assert!(v3.is_immediately_after(&v2));
286 assert!(!v3.is_immediately_after(&v1));
287 }
288
289 #[test]
290 fn test_version_ordering() {
291 let v1 = Version::first();
292 let v2 = Version::new(2).unwrap();
293 let v3 = Version::new(3).unwrap();
294
295 assert!(v1 < v2);
296 assert!(v2 < v3);
297 assert!(v1 < v3);
298 assert!(v3 > v1);
299 }
300
301 #[test]
302 fn test_as_conversions() {
303 let version = Version::new(42).unwrap();
304
305 assert_eq!(version.as_u64(), 42);
306 assert_eq!(version.as_i64(), 42);
307 assert_eq!(version.as_u32(), 42);
308 }
309
310 #[test]
311 fn test_display_trait() {
312 let version = Version::new(42).unwrap();
313 assert_eq!(format!("{}", version), "42");
314 }
315
316 #[test]
317 fn test_try_from_u64() {
318 let version: Result<Version> = 5u64.try_into();
319 assert!(version.is_ok());
320 assert_eq!(version.unwrap().as_u64(), 5);
321
322 let invalid: Result<Version> = 0u64.try_into();
323 assert!(invalid.is_err());
324 }
325
326 #[test]
327 fn test_try_from_i64() {
328 let version: Result<Version> = 5i64.try_into();
329 assert!(version.is_ok());
330 assert_eq!(version.unwrap().as_u64(), 5);
331
332 let invalid: Result<Version> = 0i64.try_into();
333 assert!(invalid.is_err());
334
335 let negative: Result<Version> = (-1i64).try_into();
336 assert!(negative.is_err());
337 }
338
339 #[test]
340 fn test_try_from_u32() {
341 let version: Result<Version> = 5u32.try_into();
342 assert!(version.is_ok());
343 assert_eq!(version.unwrap().as_u64(), 5);
344
345 let invalid: Result<Version> = 0u32.try_into();
346 assert!(invalid.is_err());
347 }
348
349 #[test]
350 fn test_into_conversions() {
351 let version = Version::new(42).unwrap();
352
353 let u64_val: u64 = version.into();
354 assert_eq!(u64_val, 42);
355
356 let version = Version::new(42).unwrap();
357 let i64_val: i64 = version.into();
358 assert_eq!(i64_val, 42);
359
360 let version = Version::new(42).unwrap();
361 let u32_val: u32 = version.into();
362 assert_eq!(u32_val, 42);
363 }
364
365 #[test]
366 fn test_equality() {
367 let v1 = Version::new(1).unwrap();
368 let v2 = Version::new(1).unwrap();
369 let v3 = Version::new(2).unwrap();
370
371 assert_eq!(v1, v2);
372 assert_ne!(v1, v3);
373 }
374
375 #[test]
376 fn test_cloning() {
377 let v1 = Version::new(5).unwrap();
378 let v2 = v1; assert_eq!(v1, v2);
380 }
381
382 #[test]
383 fn test_hash_consistency() {
384 use std::collections::HashSet;
385
386 let v1 = Version::new(5).unwrap();
387 let v2 = Version::new(5).unwrap();
388
389 let mut set = HashSet::new();
390 set.insert(v1);
391
392 assert!(set.contains(&v2));
393 }
394
395 #[test]
396 fn test_serde_serialization() {
397 let version = Version::new(42).unwrap();
398
399 let json = serde_json::to_string(&version).unwrap();
401 assert_eq!(json, "42");
402
403 let deserialized: Version = serde_json::from_str(&json).unwrap();
405 assert_eq!(deserialized, version);
406 }
407
408 #[test]
409 fn test_const_first() {
410 assert_eq!(Version::FIRST.as_u64(), 1);
411 assert_eq!(Version::FIRST, Version::first());
412 }
413
414 #[test]
415 fn test_next_saturation() {
416 let max_version = Version::new_unchecked(u64::MAX);
417 let next = max_version.next();
418 assert_eq!(next.as_u64(), u64::MAX); }
420
421 #[test]
422 fn test_new_unchecked() {
423 let version = Version::new_unchecked(0);
425 assert_eq!(version.as_u64(), 0);
426 }
427}