1use serde::{Deserialize, Serialize};
8use std::fmt;
9
10macro_rules! typed_id {
11 ($(#[$meta:meta])* $name:ident) => {
12 $(#[$meta])*
13 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
14 #[serde(transparent)]
15 pub struct $name(String);
16
17 impl $name {
18 pub fn from_string(s: impl Into<String>) -> Self {
20 Self(s.into())
21 }
22
23 pub fn new_uuid() -> Self {
25 Self(uuid::Uuid::new_v4().to_string())
26 }
27
28 pub fn as_str(&self) -> &str {
30 &self.0
31 }
32 }
33
34 impl Default for $name {
35 fn default() -> Self {
36 Self::new_uuid()
37 }
38 }
39
40 impl fmt::Display for $name {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 write!(f, "{}", self.0)
43 }
44 }
45
46 impl From<String> for $name {
47 fn from(s: String) -> Self {
48 Self(s)
49 }
50 }
51
52 impl From<&str> for $name {
53 fn from(s: &str) -> Self {
54 Self(s.to_string())
55 }
56 }
57
58 impl AsRef<str> for $name {
59 fn as_ref(&self) -> &str {
60 &self.0
61 }
62 }
63 };
64}
65
66typed_id!(
67 EventId
69);
70typed_id!(
71 AgentId
73);
74typed_id!(
75 SessionId
77);
78typed_id!(
79 BranchId
81);
82typed_id!(
83 RunId
85);
86typed_id!(
87 SnapshotId
89);
90typed_id!(
91 ApprovalId
93);
94typed_id!(
95 MemoryId
97);
98typed_id!(
99 ToolRunId
101);
102typed_id!(
103 CheckpointId
105);
106typed_id!(
107 HiveTaskId
109);
110
111impl BranchId {
112 pub fn main() -> Self {
114 Self("main".to_owned())
115 }
116}
117
118#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
120#[serde(transparent)]
121pub struct BlobHash(String);
122
123impl BlobHash {
124 pub fn from_hex(hex: impl Into<String>) -> Self {
125 Self(hex.into())
126 }
127
128 pub fn as_str(&self) -> &str {
129 &self.0
130 }
131}
132
133impl fmt::Display for BlobHash {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 write!(f, "{}", self.0)
136 }
137}
138
139impl From<String> for BlobHash {
140 fn from(s: String) -> Self {
141 Self(s)
142 }
143}
144
145impl AsRef<str> for BlobHash {
146 fn as_ref(&self) -> &str {
147 &self.0
148 }
149}
150
151pub type SeqNo = u64;
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn event_id_new_is_unique() {
160 let a = EventId::new_uuid();
161 let b = EventId::new_uuid();
162 assert_ne!(a, b);
163 }
164
165 #[test]
166 fn session_id_from_string() {
167 let id = SessionId::from_string("test-session");
168 assert_eq!(id.as_str(), "test-session");
169 assert_eq!(id.to_string(), "test-session");
170 }
171
172 #[test]
173 fn branch_id_main() {
174 let id = BranchId::main();
175 assert_eq!(id.as_str(), "main");
176 }
177
178 #[test]
179 fn branch_id_from_str_trait() {
180 let id: BranchId = "dev".into();
181 assert_eq!(id.as_str(), "dev");
182 }
183
184 #[test]
185 fn typed_id_serde_roundtrip() {
186 let id = EventId::from_string("EVT001");
187 let json = serde_json::to_string(&id).unwrap();
188 assert_eq!(json, "\"EVT001\"");
189 let back: EventId = serde_json::from_str(&json).unwrap();
190 assert_eq!(id, back);
191 }
192
193 #[test]
194 fn typed_id_hash_equality() {
195 use std::collections::HashSet;
196 let a = SessionId::from_string("same");
197 let b = SessionId::from_string("same");
198 let mut set = HashSet::new();
199 set.insert(a);
200 assert!(set.contains(&b));
201 }
202
203 #[test]
204 fn blob_hash_serde_roundtrip() {
205 let hash = BlobHash::from_hex("deadbeef");
206 let json = serde_json::to_string(&hash).unwrap();
207 assert_eq!(json, "\"deadbeef\"");
208 let back: BlobHash = serde_json::from_str(&json).unwrap();
209 assert_eq!(hash, back);
210 }
211
212 #[test]
213 fn memory_id_uniqueness() {
214 let a = MemoryId::new_uuid();
215 let b = MemoryId::new_uuid();
216 assert_ne!(a, b);
217 }
218}