winterbaume_support/
state.rs1use std::collections::HashMap;
2
3use chrono::Utc;
4use thiserror::Error;
5use uuid::Uuid;
6
7use crate::types::*;
8
9const TRUSTED_ADVISOR_STATUSES: &[&str] =
10 &["none", "enqueued", "processing", "success", "abandoned"];
11
12#[derive(Debug, Default)]
13pub struct SupportState {
14 pub cases: HashMap<String, SupportCase>,
15 next_display_id: u64,
16 pub refresh_call_counts: HashMap<String, usize>,
18}
19
20impl SupportState {
21 pub fn next_refresh_status(&mut self, check_id: &str) -> &str {
22 let count = self
23 .refresh_call_counts
24 .entry(check_id.to_string())
25 .or_insert(0);
26 let status = TRUSTED_ADVISOR_STATUSES[*count % TRUSTED_ADVISOR_STATUSES.len()];
27 *count += 1;
28 status
29 }
30}
31
32#[derive(Debug, Error)]
33pub enum SupportError {
34 #[error("The requested CaseId could not be located.")]
35 CaseIdNotFound,
36}
37
38impl SupportState {
39 pub fn create_case(
40 &mut self,
41 subject: &str,
42 communication_body: &str,
43 service_code: Option<&str>,
44 severity_code: Option<&str>,
45 category_code: Option<&str>,
46 cc_email_addresses: Vec<String>,
47 language: Option<&str>,
48 ) -> Result<&SupportCase, SupportError> {
49 let case_uuid = Uuid::new_v4().to_string().replace('-', "");
50 let case_id = format!(
51 "case-{}-{}-{}",
52 &case_uuid[..12],
53 Utc::now().format("%Y"),
54 &case_uuid[12..27]
55 );
56
57 self.next_display_id += 1;
58 let display_id = format!("{}", self.next_display_id);
59
60 let support_case = SupportCase {
61 case_id: case_id.clone(),
62 display_id,
63 subject: subject.to_string(),
64 status: "opened".to_string(),
65 service_code: service_code.unwrap_or("general-info").to_string(),
66 category_code: category_code.unwrap_or("other").to_string(),
67 severity_code: severity_code.unwrap_or("normal").to_string(),
68 communication_body: communication_body.to_string(),
69 submitted_by: "user@example.com".to_string(),
70 time_created: Utc::now().to_rfc3339(),
71 cc_email_addresses,
72 language: language.unwrap_or("en").to_string(),
73 };
74
75 self.cases.insert(case_id.clone(), support_case);
76 Ok(self.cases.get(&case_id).unwrap())
77 }
78
79 pub fn describe_cases(
80 &self,
81 case_id_list: Option<&[String]>,
82 include_resolved: bool,
83 ) -> Vec<&SupportCase> {
84 self.cases
85 .values()
86 .filter(|c| {
87 if !include_resolved && c.status == "resolved" {
88 return false;
89 }
90 if let Some(ids) = case_id_list
91 && !ids.is_empty()
92 {
93 return ids.iter().any(|id| id == &c.case_id);
94 }
95 true
96 })
97 .collect()
98 }
99
100 pub fn resolve_case(
101 &mut self,
102 case_id: Option<&str>,
103 ) -> Result<(String, String), SupportError> {
104 let case_id = match case_id {
105 Some(id) => id.to_string(),
106 None => {
107 match self
109 .cases
110 .values()
111 .filter(|c| c.status != "resolved")
112 .last()
113 {
114 Some(c) => c.case_id.clone(),
115 None => {
116 return Err(SupportError::CaseIdNotFound);
117 }
118 }
119 }
120 };
121
122 match self.cases.get_mut(&case_id) {
123 Some(c) => {
124 let initial_status = c.status.clone();
125 c.status = "resolved".to_string();
126 Ok((initial_status, "resolved".to_string()))
127 }
128 None => Err(SupportError::CaseIdNotFound),
129 }
130 }
131
132 pub fn describe_services(&self, service_code_list: Option<&[String]>) -> Vec<SupportService> {
133 let all_services = default_services();
134 match service_code_list {
135 Some(codes) if !codes.is_empty() => all_services
136 .into_iter()
137 .filter(|s| codes.iter().any(|c| c == &s.code))
138 .collect(),
139 _ => all_services,
140 }
141 }
142}
143
144fn default_services() -> Vec<SupportService> {
145 vec![
146 SupportService {
147 code: "amazon-elastic-compute-cloud-linux".to_string(),
148 name: "Amazon Elastic Compute Cloud (Linux)".to_string(),
149 categories: vec![
150 SupportCategory {
151 code: "general-info".to_string(),
152 name: "General Info".to_string(),
153 },
154 SupportCategory {
155 code: "instance-issue".to_string(),
156 name: "Instance Issue".to_string(),
157 },
158 ],
159 },
160 SupportService {
161 code: "amazon-simple-storage-service".to_string(),
162 name: "Amazon Simple Storage Service".to_string(),
163 categories: vec![SupportCategory {
164 code: "general-info".to_string(),
165 name: "General Info".to_string(),
166 }],
167 },
168 SupportService {
169 code: "general-info".to_string(),
170 name: "General Info".to_string(),
171 categories: vec![SupportCategory {
172 code: "other".to_string(),
173 name: "Other".to_string(),
174 }],
175 },
176 ]
177}