Skip to main content

sidebyside_core/
ids.rs

1//! ID types for the Sidebyside SDK
2//!
3//! These types provide type-safe identifiers for various entities in the system.
4
5use serde::{Deserialize, Serialize};
6use std::fmt;
7use uuid::Uuid;
8
9use crate::error::CoreError;
10
11/// A unique identifier for a workflow
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct WorkflowId(String);
14
15impl WorkflowId {
16    /// Create a new workflow ID from a string
17    ///
18    /// # Errors
19    /// Returns an error if the string is empty
20    pub fn new(id: impl Into<String>) -> Result<Self, CoreError> {
21        let id = id.into();
22        if id.is_empty() {
23            return Err(CoreError::InvalidId("WorkflowId cannot be empty".to_string()));
24        }
25        Ok(Self(id))
26    }
27
28    /// Generate a new random workflow ID
29    #[must_use]
30    pub fn generate() -> Self {
31        Self(Uuid::new_v4().to_string())
32    }
33
34    /// Get the inner string value
35    #[must_use]
36    pub fn as_str(&self) -> &str {
37        &self.0
38    }
39}
40
41impl fmt::Display for WorkflowId {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        write!(f, "{}", self.0)
44    }
45}
46
47/// A unique identifier for a plan
48#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
49pub struct PlanId(String);
50
51impl PlanId {
52    /// Create a new plan ID from a string
53    ///
54    /// # Errors
55    /// Returns an error if the string is empty
56    pub fn new(id: impl Into<String>) -> Result<Self, CoreError> {
57        let id = id.into();
58        if id.is_empty() {
59            return Err(CoreError::InvalidId("PlanId cannot be empty".to_string()));
60        }
61        Ok(Self(id))
62    }
63
64    /// Generate a new random plan ID
65    #[must_use]
66    pub fn generate() -> Self {
67        Self(Uuid::new_v4().to_string())
68    }
69
70    /// Get the inner string value
71    #[must_use]
72    pub fn as_str(&self) -> &str {
73        &self.0
74    }
75}
76
77impl fmt::Display for PlanId {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        write!(f, "{}", self.0)
80    }
81}
82
83/// A unique identifier for a step within a plan
84#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
85pub struct StepId(String);
86
87impl StepId {
88    /// Create a new step ID from a string
89    ///
90    /// # Errors
91    /// Returns an error if the string is empty
92    pub fn new(id: impl Into<String>) -> Result<Self, CoreError> {
93        let id = id.into();
94        if id.is_empty() {
95            return Err(CoreError::InvalidId("StepId cannot be empty".to_string()));
96        }
97        Ok(Self(id))
98    }
99
100    /// Generate a new random step ID
101    #[must_use]
102    pub fn generate() -> Self {
103        Self(Uuid::new_v4().to_string())
104    }
105
106    /// Get the inner string value
107    #[must_use]
108    pub fn as_str(&self) -> &str {
109        &self.0
110    }
111}
112
113impl fmt::Display for StepId {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        write!(f, "{}", self.0)
116    }
117}
118
119/// A unique identifier for a plan execution
120#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
121pub struct ExecutionId(String);
122
123impl ExecutionId {
124    /// Create a new execution ID from a string
125    ///
126    /// # Errors
127    /// Returns an error if the string is empty
128    pub fn new(id: impl Into<String>) -> Result<Self, CoreError> {
129        let id = id.into();
130        if id.is_empty() {
131            return Err(CoreError::InvalidId("ExecutionId cannot be empty".to_string()));
132        }
133        Ok(Self(id))
134    }
135
136    /// Generate a new random execution ID
137    #[must_use]
138    pub fn generate() -> Self {
139        Self(Uuid::new_v4().to_string())
140    }
141
142    /// Get the inner string value
143    #[must_use]
144    pub fn as_str(&self) -> &str {
145        &self.0
146    }
147}
148
149impl fmt::Display for ExecutionId {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        write!(f, "{}", self.0)
152    }
153}
154
155/// A unique identifier for a decision point
156#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
157pub struct DecisionId(String);
158
159impl DecisionId {
160    /// Create a new decision ID from a string
161    ///
162    /// # Errors
163    /// Returns an error if the string is empty
164    pub fn new(id: impl Into<String>) -> Result<Self, CoreError> {
165        let id = id.into();
166        if id.is_empty() {
167            return Err(CoreError::InvalidId("DecisionId cannot be empty".to_string()));
168        }
169        Ok(Self(id))
170    }
171
172    /// Generate a new random decision ID
173    #[must_use]
174    pub fn generate() -> Self {
175        Self(Uuid::new_v4().to_string())
176    }
177
178    /// Get the inner string value
179    #[must_use]
180    pub fn as_str(&self) -> &str {
181        &self.0
182    }
183}
184
185impl fmt::Display for DecisionId {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        write!(f, "{}", self.0)
188    }
189}
190
191#[cfg(test)]
192mod tests {
193    use super::*;
194
195    #[test]
196    fn test_workflow_id_new() {
197        let id = WorkflowId::new("test-workflow").unwrap();
198        assert_eq!(id.as_str(), "test-workflow");
199    }
200
201    #[test]
202    fn test_workflow_id_empty() {
203        let result = WorkflowId::new("");
204        assert!(result.is_err());
205    }
206
207    #[test]
208    fn test_workflow_id_generate() {
209        let id1 = WorkflowId::generate();
210        let id2 = WorkflowId::generate();
211        assert_ne!(id1, id2);
212    }
213
214    #[test]
215    fn test_plan_id_new() {
216        let id = PlanId::new("test-plan").unwrap();
217        assert_eq!(id.as_str(), "test-plan");
218    }
219
220    #[test]
221    fn test_step_id_new() {
222        let id = StepId::new("test-step").unwrap();
223        assert_eq!(id.as_str(), "test-step");
224    }
225
226    #[test]
227    fn test_execution_id_new() {
228        let id = ExecutionId::new("test-execution").unwrap();
229        assert_eq!(id.as_str(), "test-execution");
230    }
231
232    #[test]
233    fn test_decision_id_new() {
234        let id = DecisionId::new("test-decision").unwrap();
235        assert_eq!(id.as_str(), "test-decision");
236    }
237}