Skip to main content

ironflow_store/entities/
event_kind.rs

1//! Strongly-typed event kind enum for domain events.
2
3use serde::{Deserialize, Serialize};
4use strum::{Display, EnumString, IntoStaticStr};
5
6/// Strongly-typed event kind matching domain event variants.
7///
8/// Serializes to/from `snake_case` strings (e.g. `"run_status_changed"`).
9///
10/// # Examples
11///
12/// ```
13/// use ironflow_store::entities::EventKind;
14///
15/// let kind: EventKind = "run_status_changed".parse().unwrap();
16/// assert_eq!(kind, EventKind::RunStatusChanged);
17/// assert_eq!(kind.as_str(), "run_status_changed");
18/// ```
19#[derive(
20    Debug,
21    Clone,
22    Copy,
23    PartialEq,
24    Eq,
25    Hash,
26    Serialize,
27    Deserialize,
28    Display,
29    EnumString,
30    IntoStaticStr,
31)]
32#[serde(rename_all = "snake_case")]
33#[strum(serialize_all = "snake_case")]
34#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
35pub enum EventKind {
36    /// A new run was created.
37    RunCreated,
38    /// A run changed status.
39    RunStatusChanged,
40    /// A run failed.
41    RunFailed,
42    /// A step completed successfully.
43    StepCompleted,
44    /// A step failed.
45    StepFailed,
46    /// A run is waiting for human approval.
47    ApprovalRequested,
48    /// A run was approved.
49    ApprovalGranted,
50    /// A run was rejected.
51    ApprovalRejected,
52    /// A log line was emitted by a running step.
53    LogLine,
54    /// A user signed in.
55    UserSignedIn,
56    /// A new user signed up.
57    UserSignedUp,
58    /// A user signed out.
59    UserSignedOut,
60}
61
62impl EventKind {
63    /// All known event kinds.
64    pub const ALL: &'static [EventKind] = &[
65        Self::RunCreated,
66        Self::RunStatusChanged,
67        Self::RunFailed,
68        Self::StepCompleted,
69        Self::StepFailed,
70        Self::ApprovalRequested,
71        Self::ApprovalGranted,
72        Self::ApprovalRejected,
73        Self::LogLine,
74        Self::UserSignedIn,
75        Self::UserSignedUp,
76        Self::UserSignedOut,
77    ];
78
79    /// Returns the wire-format string for this kind.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// use ironflow_store::entities::EventKind;
85    ///
86    /// assert_eq!(EventKind::RunCreated.as_str(), "run_created");
87    /// assert_eq!(EventKind::StepFailed.as_str(), "step_failed");
88    /// ```
89    pub fn as_str(self) -> &'static str {
90        self.into()
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn all_variants_roundtrip_via_str() {
100        for kind in EventKind::ALL {
101            let s = kind.as_str();
102            let parsed: EventKind = s.parse().unwrap();
103            assert_eq!(*kind, parsed);
104        }
105    }
106
107    #[test]
108    fn all_has_correct_count() {
109        assert_eq!(EventKind::ALL.len(), 12);
110    }
111
112    #[test]
113    fn display_matches_as_str() {
114        for kind in EventKind::ALL {
115            assert_eq!(kind.to_string(), kind.as_str());
116        }
117    }
118
119    #[test]
120    fn parse_unknown_returns_error() {
121        assert!("bogus".parse::<EventKind>().is_err());
122    }
123
124    #[test]
125    fn serde_roundtrip() {
126        let kind = EventKind::RunStatusChanged;
127        let json = serde_json::to_string(&kind).unwrap();
128        assert_eq!(json, "\"run_status_changed\"");
129        let back: EventKind = serde_json::from_str(&json).unwrap();
130        assert_eq!(back, kind);
131    }
132
133    #[test]
134    fn serde_all_variants() {
135        for kind in EventKind::ALL {
136            let json = serde_json::to_string(kind).unwrap();
137            let back: EventKind = serde_json::from_str(&json).unwrap();
138            assert_eq!(*kind, back);
139        }
140    }
141}