1use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
7pub struct ContentHash(pub String);
8
9impl ContentHash {
10 pub fn of(bytes: &[u8]) -> Self {
11 Self(blake3::hash(bytes).to_hex().to_string())
12 }
13}
14
15#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
18pub struct Revision {
19 pub counter: u64,
20 pub hash: ContentHash,
21}
22
23impl Revision {
24 pub fn initial(body: &[u8]) -> Self {
26 Self {
27 counter: 0,
28 hash: ContentHash::of(body),
29 }
30 }
31
32 pub fn next(&self, body: &[u8]) -> Self {
34 Self {
35 counter: self.counter + 1,
36 hash: ContentHash::of(body),
37 }
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn hash_is_stable_and_distinct() {
47 assert_eq!(ContentHash::of(b"abc"), ContentHash::of(b"abc"));
48 assert_ne!(ContentHash::of(b"abc"), ContentHash::of(b"abd"));
49 }
50
51 #[test]
52 fn next_increments_counter_and_rehashes() {
53 let r0 = Revision::initial(b"v1");
54 let r1 = r0.next(b"v2");
55 assert_eq!(r0.counter, 0);
56 assert_eq!(r1.counter, 1);
57 assert_ne!(r0.hash, r1.hash);
58 }
59}