1use std::sync::Arc;
14
15use serde::Serialize;
16
17use crate::workflow::Flow;
18
19#[derive(Clone)]
39pub enum StepId {
40 WithFlow {
42 flow: Arc<Flow>,
44 index: usize,
46 },
47 Standalone {
49 name: String,
51 index: usize,
53 },
54}
55
56impl StepId {
57 pub fn for_step(flow: Arc<Flow>, index: usize) -> Self {
62 Self::WithFlow { flow, index }
63 }
64
65 pub fn new(name: String, index: usize) -> Self {
71 Self::Standalone { name, index }
72 }
73
74 pub fn index(&self) -> usize {
76 match self {
77 Self::WithFlow { index, .. } => *index,
78 Self::Standalone { index, .. } => *index,
79 }
80 }
81
82 pub fn name(&self) -> &str {
84 match self {
85 Self::WithFlow { flow, index } => &flow.steps[*index].id,
86 Self::Standalone { name, .. } => name,
87 }
88 }
89
90 pub fn to_standalone(&self) -> Self {
95 match self {
96 Self::WithFlow { flow, index } => Self::Standalone {
97 name: flow.steps[*index].id.clone(),
98 index: *index,
99 },
100 Self::Standalone { name, index } => Self::Standalone {
101 name: name.clone(),
102 index: *index,
103 },
104 }
105 }
106
107 pub fn into_standalone(self) -> Self {
112 match self {
113 Self::WithFlow { flow, index } => Self::Standalone {
114 name: flow.steps[index].id.clone(),
115 index,
116 },
117 standalone @ Self::Standalone { .. } => standalone,
118 }
119 }
120
121 pub fn has_flow(&self) -> bool {
123 matches!(self, Self::WithFlow { .. })
124 }
125
126 pub fn flow(&self) -> Option<&Arc<Flow>> {
128 match self {
129 Self::WithFlow { flow, .. } => Some(flow),
130 Self::Standalone { .. } => None,
131 }
132 }
133}
134
135impl PartialEq for StepId {
138 fn eq(&self, other: &Self) -> bool {
139 self.index() == other.index() && self.name() == other.name()
140 }
141}
142
143impl Eq for StepId {}
144
145impl std::hash::Hash for StepId {
146 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
147 self.index().hash(state);
148 self.name().hash(state);
149 }
150}
151
152impl PartialOrd for StepId {
153 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
154 Some(self.cmp(other))
155 }
156}
157
158impl Ord for StepId {
159 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
160 self.index().cmp(&other.index())
161 }
162}
163
164impl std::fmt::Display for StepId {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 write!(f, "{}", self.name())
167 }
168}
169
170impl std::fmt::Debug for StepId {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 f.debug_struct("StepId")
173 .field("index", &self.index())
174 .field("name", &self.name())
175 .finish()
176 }
177}
178
179impl Serialize for StepId {
182 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
183 where
184 S: serde::Serializer,
185 {
186 serializer.serialize_str(self.name())
187 }
188}
189
190#[cfg(test)]
196mod tests {
197 use super::*;
198 use crate::ValueExpr;
199 use crate::workflow::builders::{FlowBuilder, StepBuilder};
200 use serde_json::json;
201
202 fn create_test_flow() -> Arc<Flow> {
203 let step_one = StepBuilder::new("step_one")
204 .component("/builtin/eval")
205 .input_literal(json!({"expression": "1 + 1"}))
206 .build();
207 let step_two = StepBuilder::new("step_two")
208 .component("/builtin/eval")
209 .input_literal(json!({"expression": "2 + 2"}))
210 .build();
211
212 let flow = FlowBuilder::new()
213 .step(step_one)
214 .step(step_two)
215 .output(ValueExpr::step_output("step_two"))
216 .build();
217
218 Arc::new(flow)
219 }
220
221 #[test]
222 fn test_for_step_creation() {
223 let flow = create_test_flow();
224 let step_id = StepId::for_step(flow.clone(), 0);
225
226 assert_eq!(step_id.index(), 0);
227 assert_eq!(step_id.name(), "step_one");
228 assert!(step_id.has_flow());
229 }
230
231 #[test]
232 fn test_standalone_creation() {
233 let step_id = StepId::new("my_step".to_string(), 5);
234
235 assert_eq!(step_id.index(), 5);
236 assert_eq!(step_id.name(), "my_step");
237 assert!(!step_id.has_flow());
238 }
239
240 #[test]
241 fn test_to_standalone() {
242 let flow = create_test_flow();
243 let step_id = StepId::for_step(flow, 1);
244 let standalone = step_id.to_standalone();
245
246 assert_eq!(standalone.index(), 1);
247 assert_eq!(standalone.name(), "step_two");
248 assert!(!standalone.has_flow());
249 }
250
251 #[test]
252 fn test_into_standalone() {
253 let flow = create_test_flow();
254 let step_id = StepId::for_step(flow, 0);
255 let standalone = step_id.into_standalone();
256
257 assert_eq!(standalone.index(), 0);
258 assert_eq!(standalone.name(), "step_one");
259 assert!(!standalone.has_flow());
260 }
261
262 #[test]
263 fn test_equality_across_variants() {
264 let flow = create_test_flow();
265 let with_flow = StepId::for_step(flow, 0);
266 let standalone = StepId::new("step_one".to_string(), 0);
267
268 assert_eq!(with_flow, standalone);
269 }
270
271 #[test]
272 fn test_inequality_different_index() {
273 let step1 = StepId::new("step".to_string(), 0);
274 let step2 = StepId::new("step".to_string(), 1);
275
276 assert_ne!(step1, step2);
277 }
278
279 #[test]
280 fn test_inequality_different_name() {
281 let step1 = StepId::new("step_a".to_string(), 0);
282 let step2 = StepId::new("step_b".to_string(), 0);
283
284 assert_ne!(step1, step2);
285 }
286
287 #[test]
288 fn test_ordering() {
289 let step0 = StepId::new("z".to_string(), 0);
290 let step1 = StepId::new("a".to_string(), 1);
291 let step2 = StepId::new("m".to_string(), 2);
292
293 assert!(step0 < step1);
294 assert!(step1 < step2);
295 }
296
297 #[test]
298 fn test_hash_consistency() {
299 use std::collections::hash_map::DefaultHasher;
300 use std::hash::{Hash, Hasher};
301
302 let flow = create_test_flow();
303 let with_flow = StepId::for_step(flow, 0);
304 let standalone = StepId::new("step_one".to_string(), 0);
305
306 let mut hasher1 = DefaultHasher::new();
307 with_flow.hash(&mut hasher1);
308 let hash1 = hasher1.finish();
309
310 let mut hasher2 = DefaultHasher::new();
311 standalone.hash(&mut hasher2);
312 let hash2 = hasher2.finish();
313
314 assert_eq!(hash1, hash2);
315 }
316
317 #[test]
318 fn test_serialize_as_string() {
319 let step_id = StepId::new("my_step".to_string(), 5);
320 let json = serde_json::to_string(&step_id).unwrap();
321 assert_eq!(json, "\"my_step\"");
323 }
324
325 #[test]
326 fn test_serialize_with_flow() {
327 let flow = create_test_flow();
329 let step_id = StepId::for_step(flow, 1);
330 let json = serde_json::to_string(&step_id).unwrap();
331 assert_eq!(json, "\"step_two\"");
332 }
333
334 #[test]
339 fn test_display() {
340 let step_id = StepId::new("my_step".to_string(), 3);
341 assert_eq!(format!("{}", step_id), "my_step");
342 }
343
344 #[test]
345 fn test_debug() {
346 let step_id = StepId::new("my_step".to_string(), 3);
347 let debug_str = format!("{:?}", step_id);
348 assert!(debug_str.contains("index: 3"));
349 assert!(debug_str.contains("name: \"my_step\""));
350 }
351}