1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
5pub struct Revision {
6 pub uuid: String,
7 pub lastmod: u64,
8}
9
10impl Revision {
11 pub fn new(uuid: impl Into<String>, lastmod: u64) -> Self {
12 Self {
13 uuid: uuid.into(),
14 lastmod,
15 }
16 }
17
18 pub fn etag(&self) -> String {
19 format!("\"{}-{}\"", self.uuid, self.lastmod)
20 }
21
22 pub fn supersedes(&self, other: &Revision) -> bool {
23 self.uuid != other.uuid || self.lastmod > other.lastmod
24 }
25}
26
27impl fmt::Display for Revision {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 write!(f, "{}:{}", self.uuid, self.lastmod)
30 }
31}
32
33#[cfg(test)]
34mod tests {
35 use super::*;
36
37 #[test]
38 fn etag_is_quoted() {
39 let rev = Revision::new("abc", 42);
40 assert_eq!(rev.etag(), "\"abc-42\"");
41 }
42
43 #[test]
44 fn newer_lastmod_supersedes() {
45 let old = Revision::new("abc", 41);
46 let new = Revision::new("abc", 42);
47 assert!(new.supersedes(&old));
48 assert!(!old.supersedes(&new));
49 }
50
51 #[test]
52 fn different_database_always_supersedes() {
53 let a = Revision::new("abc", 100);
54 let b = Revision::new("xyz", 1);
55 assert!(b.supersedes(&a));
56 }
57}