Skip to main content

winterbaume_ec2instanceconnect/
views.rs

1//! Serde-compatible view types for EC2 Instance Connect state snapshots.
2
3use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6use winterbaume_core::{StateChangeNotifier, StateViewError, StatefulService};
7
8use crate::handlers::Ec2InstanceConnectService;
9use crate::state::{Ec2InstanceConnectEndpoint, Ec2InstanceConnectState};
10
11/// Serializable view of the EC2 Instance Connect state.
12#[derive(Debug, Clone, Serialize, Deserialize, Default)]
13pub struct Ec2InstanceConnectStateView {
14    /// Endpoints keyed by instance connect endpoint ID.
15    #[serde(default)]
16    pub endpoints: HashMap<String, EndpointView>,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct EndpointView {
21    pub instance_connect_endpoint_id: String,
22    pub subnet_id: String,
23    pub vpc_id: String,
24    pub security_group_ids: Vec<String>,
25    pub state: String,
26    #[serde(default)]
27    pub dns_name: Option<String>,
28    #[serde(default)]
29    pub fips_dns_name: Option<String>,
30    pub network_interface_ids: Vec<String>,
31    pub owner_id: String,
32    pub availability_zone: String,
33    pub created_at: String,
34    #[serde(default)]
35    pub tags: HashMap<String, String>,
36}
37
38// --- From internal types to view types ---
39
40impl From<&Ec2InstanceConnectState> for Ec2InstanceConnectStateView {
41    fn from(state: &Ec2InstanceConnectState) -> Self {
42        Ec2InstanceConnectStateView {
43            endpoints: state
44                .endpoints
45                .iter()
46                .map(|(k, v)| {
47                    (
48                        k.clone(),
49                        EndpointView {
50                            instance_connect_endpoint_id: v.instance_connect_endpoint_id.clone(),
51                            subnet_id: v.subnet_id.clone(),
52                            vpc_id: v.vpc_id.clone(),
53                            security_group_ids: v.security_group_ids.clone(),
54                            state: v.state.clone(),
55                            dns_name: v.dns_name.clone(),
56                            fips_dns_name: v.fips_dns_name.clone(),
57                            network_interface_ids: v.network_interface_ids.clone(),
58                            owner_id: v.owner_id.clone(),
59                            availability_zone: v.availability_zone.clone(),
60                            created_at: v.created_at.clone(),
61                            tags: v.tags.clone(),
62                        },
63                    )
64                })
65                .collect(),
66        }
67    }
68}
69
70// --- From view types to internal types ---
71
72impl From<Ec2InstanceConnectStateView> for Ec2InstanceConnectState {
73    fn from(view: Ec2InstanceConnectStateView) -> Self {
74        Ec2InstanceConnectState {
75            endpoints: view
76                .endpoints
77                .into_iter()
78                .map(|(k, v)| {
79                    (
80                        k,
81                        Ec2InstanceConnectEndpoint {
82                            instance_connect_endpoint_id: v.instance_connect_endpoint_id,
83                            subnet_id: v.subnet_id,
84                            vpc_id: v.vpc_id,
85                            security_group_ids: v.security_group_ids,
86                            state: v.state,
87                            dns_name: v.dns_name,
88                            fips_dns_name: v.fips_dns_name,
89                            network_interface_ids: v.network_interface_ids,
90                            owner_id: v.owner_id,
91                            availability_zone: v.availability_zone,
92                            created_at: v.created_at,
93                            tags: v.tags,
94                        },
95                    )
96                })
97                .collect(),
98        }
99    }
100}
101
102// --- StatefulService implementation ---
103
104impl StatefulService for Ec2InstanceConnectService {
105    type StateView = Ec2InstanceConnectStateView;
106
107    async fn snapshot(&self, account_id: &str, region: &str) -> Self::StateView {
108        let state = self.state.get(account_id, region);
109        let guard = state.read().await;
110        Ec2InstanceConnectStateView::from(&*guard)
111    }
112
113    async fn restore(
114        &self,
115        account_id: &str,
116        region: &str,
117        view: Self::StateView,
118    ) -> Result<(), StateViewError> {
119        let state = self.state.get(account_id, region);
120        {
121            let mut guard = state.write().await;
122            *guard = Ec2InstanceConnectState::from(view);
123        }
124        self.notify_state_changed(account_id, region).await;
125        Ok(())
126    }
127
128    async fn merge(
129        &self,
130        account_id: &str,
131        region: &str,
132        view: Self::StateView,
133    ) -> Result<(), StateViewError> {
134        let state = self.state.get(account_id, region);
135        {
136            let mut guard = state.write().await;
137            let incoming = Ec2InstanceConnectState::from(view);
138            for (k, v) in incoming.endpoints {
139                guard.endpoints.insert(k, v);
140            }
141        }
142        self.notify_state_changed(account_id, region).await;
143        Ok(())
144    }
145
146    fn notifier(&self) -> &StateChangeNotifier<Self::StateView> {
147        &self.notifier
148    }
149}