rusty_cdk_core/stack/
builder.rs1use crate::stack::{Resource, Stack};
2use std::error::Error;
3use std::fmt::{Debug, Display, Formatter};
4
5#[derive(Debug)]
6pub enum StackBuilderError {
7 MissingPermissionsForRole(Vec<String>),
8 DuplicateIds(Vec<String>),
9 DuplicateResourceIds(Vec<String>),
10}
11
12impl Display for StackBuilderError {
13 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
14 match self {
15 StackBuilderError::MissingPermissionsForRole(info) => {
16 let gathered_info = info.join(";");
17 f.write_fmt(format_args!(
18 "one or more roles seem to be missing permission to access services: `{}`?",
19 gathered_info
20 ))
21 }
22 StackBuilderError::DuplicateIds(info) => {
23 let gathered_info = info.join(";");
24 f.write_fmt(format_args!(
25 "ids should be unique, but the following duplicates were detected: `{}`",
26 gathered_info
27 ))
28 }
29 StackBuilderError::DuplicateResourceIds(info) => {
30 let gathered_info = info.join(";");
31 f.write_fmt(format_args!(
32 "duplicate resource ids were detected (`{}`), rerunning this command should generate new ones",
33 gathered_info
34 ))
35 }
36 }
37 }
38}
39
40impl Error for StackBuilderError {}
41
42pub struct StackBuilder {
70 resources: Vec<Resource>,
71 tags: Vec<(String, String)>,
72}
73
74impl Default for StackBuilder {
75 fn default() -> Self {
76 Self::new()
77 }
78}
79
80impl StackBuilder {
81 pub fn new() -> Self {
82 Self {
83 resources: vec![],
84 tags: vec![],
85 }
86 }
87
88 pub fn add_resource<T: Into<Resource>>(&mut self, resource: T) {
89 let resource = resource.into();
90 self.resources.push(resource);
91 }
92
93 pub fn add_tag<T: Into<String>>(mut self, key: T, value: T) -> Self {
94 self.tags.push((key.into(), value.into()));
95 self
96 }
97
98 pub fn build(self) -> Result<Stack, StackBuilderError> {
102 let (ids, resource_ids) = self.resources
103 .iter()
104 .map(|r| (r.get_id().to_string(), r.get_resource_id().to_string()))
105 .collect::<(Vec<_>, Vec<_>)>();
106
107 let duplicate_ids = Self::check_for_duplicate_ids(ids);
108 let resource_ids = Self::check_for_duplicate_ids(resource_ids);
109
110 if !duplicate_ids.is_empty() {
111 return Err(StackBuilderError::DuplicateIds(
112 duplicate_ids,
113 ));
114 }
115 if !resource_ids.is_empty() {
116 return Err(StackBuilderError::DuplicateResourceIds(
117 resource_ids,
118 ));
119 }
120
121 let roles_with_potentially_missing_services: Vec<_> = self.check_for_roles_with_missing_permissions();
122
123 if !roles_with_potentially_missing_services.is_empty() {
124 return Err(StackBuilderError::MissingPermissionsForRole(
125 roles_with_potentially_missing_services,
126 ));
127 }
128
129
130 let metadata = self
131 .resources
132 .iter()
133 .map(|r| (r.get_id().to_string(), r.get_resource_id().to_string()))
134 .collect();
135
136
137
138 let resources = self.resources.into_iter().map(|r| (r.get_resource_id().to_string(), r)).collect();
139 Ok(Stack {
140 resource_ids_to_replace: vec![],
141 tags: self.tags,
142 resources,
143 metadata,
144 })
145 }
146
147 fn check_for_roles_with_missing_permissions(&self) -> Vec<String> {
148 self.resources
149 .iter()
150 .filter_map(|r| match r {
151 Resource::Role(r) => {
152 if !r.potentially_missing_services.is_empty() {
153 Some(format!("{}: {}", r.resource_id, r.potentially_missing_services.join(",")))
154 } else {
155 None
156 }
157 }
158 _ => None,
159 })
160 .collect()
161 }
162
163 fn check_for_duplicate_ids(ids: Vec<String>) -> Vec<String> {
164 let results = ids.into_iter().fold((vec![], vec![]), |(mut all, mut duplicates), curr| {
165 if all.contains(&curr) && !duplicates.contains(&curr) {
166 duplicates.push(curr.clone());
167 }
168 all.push(curr);
169 return (all, duplicates);
170 });
171 results.1
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use crate::stack::StackBuilder;
178
179 #[test]
180 fn test_check_for_duplicate_ids() {
181 let duplicates = StackBuilder::check_for_duplicate_ids(vec!["bucket".to_string(), "bucket".to_string(), "topic".to_string(), "queue".to_string(), "bucket".to_string(), "table".to_string(), "topic".to_string()]);
182
183 assert_eq!(duplicates, vec!["bucket", "topic"])
184 }
185}