Skip to main content

fakecloud_cloudformation/
state.rs

1use chrono::{DateTime, Utc};
2use parking_lot::RwLock;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::sync::Arc;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct StackResource {
9    pub logical_id: String,
10    pub physical_id: String,
11    pub resource_type: String,
12    pub status: String,
13    /// For custom resources, the Lambda ARN (ServiceToken) used for invocation.
14    pub service_token: Option<String>,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct Stack {
19    pub name: String,
20    pub stack_id: String,
21    pub template: String,
22    pub status: String,
23    pub resources: Vec<StackResource>,
24    pub parameters: HashMap<String, String>,
25    pub tags: HashMap<String, String>,
26    pub created_at: DateTime<Utc>,
27    pub updated_at: Option<DateTime<Utc>>,
28    pub description: Option<String>,
29    pub notification_arns: Vec<String>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct CloudFormationState {
34    pub account_id: String,
35    pub region: String,
36    #[serde(default)]
37    pub stacks: HashMap<String, Stack>,
38}
39
40impl CloudFormationState {
41    pub fn new(account_id: &str, region: &str) -> Self {
42        Self {
43            account_id: account_id.to_string(),
44            region: region.to_string(),
45            stacks: HashMap::new(),
46        }
47    }
48
49    pub fn reset(&mut self) {
50        self.stacks.clear();
51    }
52}
53
54pub type SharedCloudFormationState =
55    Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<CloudFormationState>>>;
56
57impl fakecloud_core::multi_account::AccountState for CloudFormationState {
58    fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
59        Self::new(account_id, region)
60    }
61}
62
63pub const CLOUDFORMATION_SNAPSHOT_SCHEMA_VERSION: u32 = 2;
64
65#[derive(Debug, Serialize, Deserialize)]
66pub struct CloudFormationSnapshot {
67    pub schema_version: u32,
68    #[serde(default)]
69    pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<CloudFormationState>>,
70    #[serde(default)]
71    pub state: Option<CloudFormationState>,
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn new_initializes_empty() {
80        let state = CloudFormationState::new("123456789012", "us-east-1");
81        assert_eq!(state.account_id, "123456789012");
82        assert_eq!(state.region, "us-east-1");
83        assert!(state.stacks.is_empty());
84    }
85
86    #[test]
87    fn reset_clears_stacks() {
88        let mut state = CloudFormationState::new("123456789012", "us-east-1");
89        state.stacks.insert(
90            "s1".to_string(),
91            Stack {
92                name: "s1".to_string(),
93                stack_id: "id".to_string(),
94                template: "{}".to_string(),
95                status: "CREATE_COMPLETE".to_string(),
96                resources: vec![],
97                parameters: HashMap::new(),
98                tags: HashMap::new(),
99                created_at: Utc::now(),
100                updated_at: None,
101                description: None,
102                notification_arns: vec![],
103            },
104        );
105        state.reset();
106        assert!(state.stacks.is_empty());
107    }
108}