1use serde::{Deserialize, Serialize};
9use std::fmt;
10use uuid::Uuid;
11
12macro_rules! uuid_id {
13 ($(#[$attr:meta])* $name:ident) => {
14 $(#[$attr])*
15 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
16 pub struct $name(pub Uuid);
17
18 impl $name {
19 pub fn new() -> Self {
21 Self(Uuid::new_v4())
22 }
23
24 pub fn from_uuid(uuid: Uuid) -> Self {
26 Self(uuid)
27 }
28
29 pub fn as_uuid(&self) -> &Uuid {
31 &self.0
32 }
33 }
34
35 impl Default for $name {
36 fn default() -> Self {
37 Self::new()
38 }
39 }
40
41 impl fmt::Display for $name {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 self.0.fmt(f)
44 }
45 }
46 };
47}
48
49uuid_id!(
50 ProjectId
52);
53uuid_id!(
54 ConfigId
56);
57uuid_id!(
58 ExperimentId
60);
61uuid_id!(
62 SessionId
64);
65uuid_id!(
66 SignalId
68);
69
70#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
75pub struct AdapterId(String);
76
77impl AdapterId {
78 pub fn new(name: impl Into<String>) -> Self {
80 Self(name.into())
81 }
82
83 pub fn as_str(&self) -> &str {
85 &self.0
86 }
87}
88
89impl fmt::Display for AdapterId {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 f.write_str(&self.0)
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn project_id_roundtrips_through_json() {
101 let id = ProjectId::new();
102 let json = serde_json::to_string(&id).unwrap();
103 let back: ProjectId = serde_json::from_str(&json).unwrap();
104 assert_eq!(id, back);
105 }
106
107 #[test]
108 fn distinct_ids_are_not_equal() {
109 let a = ProjectId::new();
110 let b = ProjectId::new();
111 assert_ne!(a, b);
112 }
113
114 #[test]
115 fn from_uuid_then_as_uuid_roundtrips() {
116 let raw = Uuid::new_v4();
117 let id = SessionId::from_uuid(raw);
118 assert_eq!(id.as_uuid(), &raw);
119 }
120
121 #[test]
122 fn display_matches_inner_uuid() {
123 let raw = Uuid::new_v4();
124 let id = ConfigId::from_uuid(raw);
125 assert_eq!(format!("{id}"), format!("{raw}"));
126 }
127
128 #[test]
129 fn adapter_id_roundtrips() {
130 let id = AdapterId::new("claude-code");
131 let json = serde_json::to_string(&id).unwrap();
132 let back: AdapterId = serde_json::from_str(&json).unwrap();
133 assert_eq!(id, back);
134 assert_eq!(id.as_str(), "claude-code");
135 }
136
137 #[test]
138 fn adapter_id_display_matches_inner_string() {
139 let id = AdapterId::new("aider");
140 assert_eq!(format!("{id}"), "aider");
141 }
142
143 #[test]
144 fn distinct_id_types_are_not_interchangeable() {
145 let _project = ProjectId::new();
150 let _config = ConfigId::new();
151 }
152}