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 #[serde(default)]
35 pub state_machines: HashMap<String, StateMachine>,
36 #[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}