Skip to main content

fakecloud_stepfunctions/
state.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use chrono::{DateTime, Utc};
5use parking_lot::RwLock;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9pub type SharedStepFunctionsState =
10    Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<StepFunctionsState>>>;
11
12impl fakecloud_core::multi_account::AccountState for StepFunctionsState {
13    fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
14        Self::new(account_id, region)
15    }
16}
17
18pub const STEPFUNCTIONS_SNAPSHOT_SCHEMA_VERSION: u32 = 2;
19
20#[derive(Debug, Serialize, Deserialize)]
21pub struct StepFunctionsSnapshot {
22    pub schema_version: u32,
23    #[serde(default)]
24    pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<StepFunctionsState>>,
25    #[serde(default)]
26    pub state: Option<StepFunctionsState>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct StepFunctionsState {
31    pub account_id: String,
32    pub region: String,
33    /// State machines keyed by ARN.
34    #[serde(default)]
35    pub state_machines: HashMap<String, StateMachine>,
36    /// Executions keyed by execution ARN.
37    #[serde(default)]
38    pub executions: HashMap<String, Execution>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct StateMachine {
43    pub name: String,
44    pub arn: String,
45    pub definition: String,
46    pub role_arn: String,
47    pub machine_type: StateMachineType,
48    pub status: StateMachineStatus,
49    pub creation_date: DateTime<Utc>,
50    pub update_date: DateTime<Utc>,
51    pub tags: HashMap<String, String>,
52    pub revision_id: String,
53    pub logging_configuration: Option<Value>,
54    pub tracing_configuration: Option<Value>,
55    pub description: String,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
59pub enum StateMachineType {
60    Standard,
61    Express,
62}
63
64impl StateMachineType {
65    pub fn as_str(&self) -> &'static str {
66        match self {
67            Self::Standard => "STANDARD",
68            Self::Express => "EXPRESS",
69        }
70    }
71
72    pub fn parse(s: &str) -> Option<Self> {
73        match s {
74            "STANDARD" => Some(Self::Standard),
75            "EXPRESS" => Some(Self::Express),
76            _ => None,
77        }
78    }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
82pub enum StateMachineStatus {
83    Active,
84    Deleting,
85}
86
87impl StateMachineStatus {
88    pub fn as_str(&self) -> &'static str {
89        match self {
90            Self::Active => "ACTIVE",
91            Self::Deleting => "DELETING",
92        }
93    }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct Execution {
98    pub execution_arn: String,
99    pub state_machine_arn: String,
100    pub state_machine_name: String,
101    pub name: String,
102    pub status: ExecutionStatus,
103    pub input: Option<String>,
104    pub output: Option<String>,
105    pub start_date: DateTime<Utc>,
106    pub stop_date: Option<DateTime<Utc>>,
107    pub error: Option<String>,
108    pub cause: Option<String>,
109    pub history_events: Vec<HistoryEvent>,
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
113pub enum ExecutionStatus {
114    Running,
115    Succeeded,
116    Failed,
117    TimedOut,
118    Aborted,
119    PendingRedrive,
120}
121
122impl ExecutionStatus {
123    pub fn as_str(&self) -> &'static str {
124        match self {
125            Self::Running => "RUNNING",
126            Self::Succeeded => "SUCCEEDED",
127            Self::Failed => "FAILED",
128            Self::TimedOut => "TIMED_OUT",
129            Self::Aborted => "ABORTED",
130            Self::PendingRedrive => "PENDING_REDRIVE",
131        }
132    }
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct HistoryEvent {
137    pub id: i64,
138    pub event_type: String,
139    pub timestamp: DateTime<Utc>,
140    pub previous_event_id: i64,
141    pub details: Value,
142}
143
144impl StepFunctionsState {
145    pub fn new(account_id: &str, region: &str) -> Self {
146        Self {
147            account_id: account_id.to_string(),
148            region: region.to_string(),
149            state_machines: HashMap::new(),
150            executions: HashMap::new(),
151        }
152    }
153
154    pub fn reset(&mut self) {
155        self.state_machines.clear();
156        self.executions.clear();
157    }
158
159    pub fn state_machine_arn(&self, name: &str) -> String {
160        format!(
161            "arn:aws:states:{}:{}:stateMachine:{}",
162            self.region, self.account_id, name
163        )
164    }
165
166    pub fn execution_arn(&self, state_machine_name: &str, execution_name: &str) -> String {
167        format!(
168            "arn:aws:states:{}:{}:execution:{}:{}",
169            self.region, self.account_id, state_machine_name, execution_name
170        )
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177
178    #[test]
179    fn state_machine_type_as_str() {
180        assert_eq!(StateMachineType::Standard.as_str(), "STANDARD");
181        assert_eq!(StateMachineType::Express.as_str(), "EXPRESS");
182    }
183
184    #[test]
185    fn state_machine_type_parse() {
186        assert_eq!(
187            StateMachineType::parse("STANDARD"),
188            Some(StateMachineType::Standard)
189        );
190        assert_eq!(
191            StateMachineType::parse("EXPRESS"),
192            Some(StateMachineType::Express)
193        );
194        assert_eq!(StateMachineType::parse("bogus"), None);
195    }
196
197    #[test]
198    fn state_machine_status_as_str() {
199        assert_eq!(StateMachineStatus::Active.as_str(), "ACTIVE");
200        assert_eq!(StateMachineStatus::Deleting.as_str(), "DELETING");
201    }
202
203    #[test]
204    fn execution_status_as_str() {
205        assert_eq!(ExecutionStatus::Running.as_str(), "RUNNING");
206        assert_eq!(ExecutionStatus::Succeeded.as_str(), "SUCCEEDED");
207        assert_eq!(ExecutionStatus::Failed.as_str(), "FAILED");
208        assert_eq!(ExecutionStatus::TimedOut.as_str(), "TIMED_OUT");
209        assert_eq!(ExecutionStatus::Aborted.as_str(), "ABORTED");
210        assert_eq!(ExecutionStatus::PendingRedrive.as_str(), "PENDING_REDRIVE");
211    }
212
213    #[test]
214    fn state_machine_arn_format() {
215        let state = StepFunctionsState::new("123456789012", "us-east-1");
216        assert_eq!(
217            state.state_machine_arn("my-sm"),
218            "arn:aws:states:us-east-1:123456789012:stateMachine:my-sm"
219        );
220    }
221
222    #[test]
223    fn execution_arn_format() {
224        let state = StepFunctionsState::new("123456789012", "us-east-1");
225        assert_eq!(
226            state.execution_arn("sm", "exec-1"),
227            "arn:aws:states:us-east-1:123456789012:execution:sm:exec-1"
228        );
229    }
230
231    #[test]
232    fn state_reset_clears_all() {
233        let mut state = StepFunctionsState::new("123456789012", "us-east-1");
234        state.state_machines.insert(
235            "x".to_string(),
236            StateMachine {
237                name: "sm".to_string(),
238                arn: "arn:aws:states:us-east-1:123:stateMachine:sm".to_string(),
239                definition: "{}".to_string(),
240                role_arn: "r".to_string(),
241                machine_type: StateMachineType::Standard,
242                status: StateMachineStatus::Active,
243                creation_date: Utc::now(),
244                update_date: Utc::now(),
245                tags: HashMap::new(),
246                revision_id: "v1".to_string(),
247                logging_configuration: None,
248                tracing_configuration: None,
249                description: String::new(),
250            },
251        );
252        state.reset();
253        assert!(state.state_machines.is_empty());
254        assert!(state.executions.is_empty());
255    }
256}