aft/commands/semantic_search/
generation_token.rs1use std::fmt;
2use std::sync::OnceLock;
3
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6static PROCESS_NONCE: OnceLock<[u8; 16]> = OnceLock::new();
7
8fn get_process_nonce() -> [u8; 16] {
10 *PROCESS_NONCE.get_or_init(|| {
11 let mut nonce = [0u8; 16];
12 getrandom::fill(&mut nonce).expect("failed to generate 128-bit CSPRNG process nonce");
13 nonce
14 })
15}
16
17#[derive(Clone, PartialEq, Eq, Hash)]
23pub struct GenerationToken {
24 opaque: String,
25}
26
27impl GenerationToken {
28 pub fn new(snapshot_generation: u64) -> Self {
31 Self::new_with_str(&snapshot_generation.to_string())
32 }
33
34 pub fn new_with_str(snapshot_generation: &str) -> Self {
37 Self::new_with_nonce(snapshot_generation, get_process_nonce())
38 }
39
40 pub fn new_with_nonce(snapshot_generation: &str, nonce: [u8; 16]) -> Self {
42 let nonce_hex = u128::from_be_bytes(nonce);
43 let opaque = format!("{snapshot_generation}_{nonce_hex:032x}");
44 Self { opaque }
45 }
46
47 pub fn as_str(&self) -> &str {
49 &self.opaque
50 }
51}
52
53impl fmt::Debug for GenerationToken {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 write!(f, "GenerationToken({})", self.opaque)
56 }
57}
58
59impl fmt::Display for GenerationToken {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(f, "{}", self.opaque)
62 }
63}
64
65impl Serialize for GenerationToken {
66 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
67 where
68 S: Serializer,
69 {
70 serializer.serialize_str(&self.opaque)
71 }
72}
73
74impl<'de> Deserialize<'de> for GenerationToken {
75 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
76 where
77 D: Deserializer<'de>,
78 {
79 let s = String::deserialize(deserializer)?;
80 Ok(Self { opaque: s })
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn equality_and_uniqueness() {
90 let t1 = GenerationToken::new(42);
91 let t2 = GenerationToken::new(42);
92 let t3 = GenerationToken::new(43);
93
94 assert_eq!(t1, t2);
96 assert_ne!(t1, t3);
98
99 let n1 = [1u8; 16];
101 let n2 = [2u8; 16];
102 let tn1 = GenerationToken::new_with_nonce("42", n1);
103 let tn2 = GenerationToken::new_with_nonce("42", n2);
104 assert_ne!(tn1, tn2);
105 }
106
107 #[test]
108 fn serialization_roundtrip() {
109 let token = GenerationToken::new(100);
110 let json = serde_json::to_string(&token).unwrap();
111 let deserialized: GenerationToken = serde_json::from_str(&json).unwrap();
112 assert_eq!(token, deserialized);
113 }
114}