Skip to main content

winterbaume_support/
views.rs

1//! Serde-compatible view types for Support state snapshots.
2
3use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6use winterbaume_core::{StateChangeNotifier, StateViewError, StatefulService};
7
8use crate::handlers::SupportService;
9use crate::state::SupportState;
10
11/// Serializable view of the entire Support state for one account/region.
12#[derive(Debug, Clone, Serialize, Deserialize, Default)]
13pub struct SupportStateView {
14    /// Support cases keyed by case ID.
15    #[serde(default)]
16    pub cases: HashMap<String, SupportCaseView>,
17    /// Refresh call counts for trusted advisor checks, keyed by check ID.
18    #[serde(default)]
19    pub refresh_call_counts: HashMap<String, usize>,
20}
21
22/// Serializable view of a single support case.
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct SupportCaseView {
25    pub case_id: String,
26    pub display_id: String,
27    pub subject: String,
28    pub status: String,
29    pub service_code: String,
30    pub category_code: String,
31    pub severity_code: String,
32    pub communication_body: String,
33    pub submitted_by: String,
34    pub time_created: String,
35    #[serde(default)]
36    pub cc_email_addresses: Vec<String>,
37    pub language: String,
38}
39
40// --- From internal types to view types ---
41
42impl From<&SupportState> for SupportStateView {
43    fn from(state: &SupportState) -> Self {
44        SupportStateView {
45            cases: state
46                .cases
47                .iter()
48                .map(|(k, v)| {
49                    (
50                        k.clone(),
51                        SupportCaseView {
52                            case_id: v.case_id.clone(),
53                            display_id: v.display_id.clone(),
54                            subject: v.subject.clone(),
55                            status: v.status.clone(),
56                            service_code: v.service_code.clone(),
57                            category_code: v.category_code.clone(),
58                            severity_code: v.severity_code.clone(),
59                            communication_body: v.communication_body.clone(),
60                            submitted_by: v.submitted_by.clone(),
61                            time_created: v.time_created.clone(),
62                            cc_email_addresses: v.cc_email_addresses.clone(),
63                            language: v.language.clone(),
64                        },
65                    )
66                })
67                .collect(),
68            refresh_call_counts: state.refresh_call_counts.clone(),
69        }
70    }
71}
72
73// --- From view types to internal types ---
74
75fn support_state_from_view(view: SupportStateView) -> SupportState {
76    use crate::types::SupportCase;
77    let mut state = SupportState::default();
78    state.cases = view
79        .cases
80        .into_iter()
81        .map(|(k, v)| {
82            (
83                k,
84                SupportCase {
85                    case_id: v.case_id,
86                    display_id: v.display_id,
87                    subject: v.subject,
88                    status: v.status,
89                    service_code: v.service_code,
90                    category_code: v.category_code,
91                    severity_code: v.severity_code,
92                    communication_body: v.communication_body,
93                    submitted_by: v.submitted_by,
94                    time_created: v.time_created,
95                    cc_email_addresses: v.cc_email_addresses,
96                    language: v.language,
97                },
98            )
99        })
100        .collect();
101    state.refresh_call_counts = view.refresh_call_counts;
102    state
103}
104
105// --- StatefulService implementation ---
106
107impl StatefulService for SupportService {
108    type StateView = SupportStateView;
109
110    async fn snapshot(&self, account_id: &str, region: &str) -> Self::StateView {
111        let state = self.state.get(account_id, region);
112        let guard = state.read().await;
113        SupportStateView::from(&*guard)
114    }
115
116    async fn restore(
117        &self,
118        account_id: &str,
119        region: &str,
120        view: Self::StateView,
121    ) -> Result<(), StateViewError> {
122        let state = self.state.get(account_id, region);
123        {
124            let mut guard = state.write().await;
125            *guard = support_state_from_view(view);
126        }
127        self.notify_state_changed(account_id, region).await;
128        Ok(())
129    }
130
131    async fn merge(
132        &self,
133        account_id: &str,
134        region: &str,
135        view: Self::StateView,
136    ) -> Result<(), StateViewError> {
137        let state = self.state.get(account_id, region);
138        {
139            let mut guard = state.write().await;
140            for (k, v) in view.cases {
141                use crate::types::SupportCase;
142                guard.cases.insert(
143                    k,
144                    SupportCase {
145                        case_id: v.case_id,
146                        display_id: v.display_id,
147                        subject: v.subject,
148                        status: v.status,
149                        service_code: v.service_code,
150                        category_code: v.category_code,
151                        severity_code: v.severity_code,
152                        communication_body: v.communication_body,
153                        submitted_by: v.submitted_by,
154                        time_created: v.time_created,
155                        cc_email_addresses: v.cc_email_addresses,
156                        language: v.language,
157                    },
158                );
159            }
160            for (k, v) in view.refresh_call_counts {
161                guard.refresh_call_counts.insert(k, v);
162            }
163        }
164        self.notify_state_changed(account_id, region).await;
165        Ok(())
166    }
167
168    fn notifier(&self) -> &StateChangeNotifier<Self::StateView> {
169        &self.notifier
170    }
171}