1use serde::{Deserialize, Serialize};
6use std::borrow::Borrow;
7use std::fmt;
8use std::str::FromStr;
9use uuid::Uuid;
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
13#[serde(transparent)]
14pub struct RunId(Uuid);
15
16impl RunId {
17 pub fn new() -> Self {
18 Self(Uuid::new_v4())
19 }
20
21 pub fn as_uuid(&self) -> &Uuid {
22 &self.0
23 }
24}
25
26impl Default for RunId {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl fmt::Display for RunId {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 self.0.fmt(f)
35 }
36}
37
38impl FromStr for RunId {
39 type Err = uuid::Error;
40
41 fn from_str(s: &str) -> Result<Self, Self::Err> {
42 Ok(Self(Uuid::parse_str(s)?))
43 }
44}
45
46macro_rules! string_newtype {
47 ($(#[$meta:meta])* $name:ident) => {
48 $(#[$meta])*
49 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
50 #[serde(transparent)]
51 pub struct $name(String);
52
53 impl $name {
54 pub fn as_str(&self) -> &str {
55 &self.0
56 }
57 }
58
59 impl fmt::Display for $name {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 self.0.fmt(f)
62 }
63 }
64
65 impl From<String> for $name {
66 fn from(value: String) -> Self {
67 Self(value)
68 }
69 }
70
71 impl From<&str> for $name {
72 fn from(value: &str) -> Self {
73 Self(value.to_owned())
74 }
75 }
76
77 impl Borrow<str> for $name {
78 fn borrow(&self) -> &str {
79 &self.0
80 }
81 }
82
83 impl AsRef<str> for $name {
84 fn as_ref(&self) -> &str {
85 &self.0
86 }
87 }
88 };
89}
90
91string_newtype!(
92 MobId
94);
95
96string_newtype!(
97 FlowId
99);
100
101string_newtype!(
102 StepId
104);
105
106string_newtype!(
107 BranchId
109);
110
111string_newtype!(
112 TaskId
114);
115
116string_newtype!(
117 MeerkatId
119);
120
121string_newtype!(
122 ProfileName
124);
125
126string_newtype!(
127 FrameId
129);
130
131string_newtype!(
132 LoopInstanceId
134);
135
136string_newtype!(
137 FlowNodeId
139);
140
141string_newtype!(
142 LoopId
144);
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_run_id_roundtrip_json() {
152 let run_id = RunId::new();
153 let encoded = serde_json::to_string(&run_id).unwrap();
154 let decoded: RunId = serde_json::from_str(&encoded).unwrap();
155 assert_eq!(decoded, run_id);
156 }
157
158 #[test]
159 fn test_run_id_roundtrip_parse_display() {
160 let run_id = RunId::new();
161 let rendered = run_id.to_string();
162 let reparsed = RunId::from_str(&rendered).unwrap();
163 assert_eq!(reparsed, run_id);
164 }
165
166 #[test]
167 fn test_flow_id_roundtrip_json() {
168 let id = FlowId::from("flow-a");
169 let encoded = serde_json::to_string(&id).unwrap();
170 let decoded: FlowId = serde_json::from_str(&encoded).unwrap();
171 assert_eq!(decoded, id);
172 }
173
174 #[test]
175 fn test_step_id_roundtrip_json() {
176 let id = StepId::from("step-a");
177 let encoded = serde_json::to_string(&id).unwrap();
178 let decoded: StepId = serde_json::from_str(&encoded).unwrap();
179 assert_eq!(decoded, id);
180 }
181
182 #[test]
183 fn test_branch_id_roundtrip_json() {
184 let id = BranchId::from("branch-a");
185 let encoded = serde_json::to_string(&id).unwrap();
186 let decoded: BranchId = serde_json::from_str(&encoded).unwrap();
187 assert_eq!(decoded, id);
188 }
189
190 #[test]
191 fn test_task_id_roundtrip_json() {
192 let id = TaskId::from("task-a");
193 let encoded = serde_json::to_string(&id).unwrap();
194 let decoded: TaskId = serde_json::from_str(&encoded).unwrap();
195 assert_eq!(decoded, id);
196 }
197
198 #[test]
199 fn test_frame_id_roundtrip_json() {
200 let id = FrameId::from("frame-a");
201 let encoded = serde_json::to_string(&id).unwrap();
202 let decoded: FrameId = serde_json::from_str(&encoded).unwrap();
203 assert_eq!(decoded, id);
204 }
205
206 #[test]
207 fn test_loop_instance_id_roundtrip_json() {
208 let id = LoopInstanceId::from("loop-instance-a");
209 let encoded = serde_json::to_string(&id).unwrap();
210 let decoded: LoopInstanceId = serde_json::from_str(&encoded).unwrap();
211 assert_eq!(decoded, id);
212 }
213
214 #[test]
215 fn test_flow_node_id_roundtrip_json() {
216 let id = FlowNodeId::from("node-a");
217 let encoded = serde_json::to_string(&id).unwrap();
218 let decoded: FlowNodeId = serde_json::from_str(&encoded).unwrap();
219 assert_eq!(decoded, id);
220 }
221
222 #[test]
223 fn test_loop_id_roundtrip_json() {
224 let id = LoopId::from("loop-a");
225 let encoded = serde_json::to_string(&id).unwrap();
226 let decoded: LoopId = serde_json::from_str(&encoded).unwrap();
227 assert_eq!(decoded, id);
228 }
229
230 #[test]
231 fn test_existing_ids_roundtrip() {
232 let mob = MobId::from("mob-a");
233 let meerkat = MeerkatId::from("meerkat-a");
234 let profile = ProfileName::from("lead");
235 assert_eq!(
236 serde_json::from_str::<MobId>(&serde_json::to_string(&mob).unwrap()).unwrap(),
237 mob
238 );
239 assert_eq!(
240 serde_json::from_str::<MeerkatId>(&serde_json::to_string(&meerkat).unwrap()).unwrap(),
241 meerkat
242 );
243 assert_eq!(
244 serde_json::from_str::<ProfileName>(&serde_json::to_string(&profile).unwrap()).unwrap(),
245 profile
246 );
247 }
248}