fakecloud_cloudformation/
xml_responses.rs1use crate::state::{Stack, StackResource};
2
3fn xml_escape(s: &str) -> String {
4 s.replace('&', "&")
5 .replace('<', "<")
6 .replace('>', ">")
7 .replace('"', """)
8}
9
10pub fn create_stack_response(stack_id: &str, request_id: &str) -> String {
11 format!(
12 r#"<CreateStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
13 <CreateStackResult>
14 <StackId>{stack_id}</StackId>
15 </CreateStackResult>
16 <ResponseMetadata>
17 <RequestId>{request_id}</RequestId>
18 </ResponseMetadata>
19</CreateStackResponse>"#,
20 stack_id = xml_escape(stack_id),
21 request_id = xml_escape(request_id),
22 )
23}
24
25pub fn update_stack_response(stack_id: &str, request_id: &str) -> String {
26 format!(
27 r#"<UpdateStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
28 <UpdateStackResult>
29 <StackId>{stack_id}</StackId>
30 </UpdateStackResult>
31 <ResponseMetadata>
32 <RequestId>{request_id}</RequestId>
33 </ResponseMetadata>
34</UpdateStackResponse>"#,
35 stack_id = xml_escape(stack_id),
36 request_id = xml_escape(request_id),
37 )
38}
39
40pub fn delete_stack_response(request_id: &str) -> String {
41 format!(
42 r#"<DeleteStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
43 <ResponseMetadata>
44 <RequestId>{request_id}</RequestId>
45 </ResponseMetadata>
46</DeleteStackResponse>"#,
47 request_id = xml_escape(request_id),
48 )
49}
50
51pub fn describe_stacks_response(stacks: &[Stack], request_id: &str) -> String {
52 let members: String = stacks
53 .iter()
54 .map(stack_member_xml)
55 .collect::<Vec<_>>()
56 .join("\n");
57
58 format!(
59 r#"<DescribeStacksResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
60 <DescribeStacksResult>
61 <Stacks>
62{members}
63 </Stacks>
64 </DescribeStacksResult>
65 <ResponseMetadata>
66 <RequestId>{request_id}</RequestId>
67 </ResponseMetadata>
68</DescribeStacksResponse>"#,
69 request_id = xml_escape(request_id),
70 )
71}
72
73fn stack_member_xml(stack: &Stack) -> String {
74 let tags_xml = if stack.tags.is_empty() {
75 String::new()
76 } else {
77 let tags: String = stack
78 .tags
79 .iter()
80 .map(|(k, v)| {
81 format!(
82 " <member>\n <Key>{}</Key>\n <Value>{}</Value>\n </member>",
83 xml_escape(k),
84 xml_escape(v),
85 )
86 })
87 .collect::<Vec<_>>()
88 .join("\n");
89 format!("\n <Tags>\n{tags}\n </Tags>")
90 };
91
92 let params_xml = if stack.parameters.is_empty() {
93 String::new()
94 } else {
95 let params: String = stack
96 .parameters
97 .iter()
98 .map(|(k, v)| {
99 format!(
100 " <member>\n <ParameterKey>{}</ParameterKey>\n <ParameterValue>{}</ParameterValue>\n </member>",
101 xml_escape(k),
102 xml_escape(v),
103 )
104 })
105 .collect::<Vec<_>>()
106 .join("\n");
107 format!("\n <Parameters>\n{params}\n </Parameters>")
108 };
109
110 let description_xml = stack
111 .description
112 .as_ref()
113 .map(|d| format!("\n <Description>{}</Description>", xml_escape(d)))
114 .unwrap_or_default();
115
116 format!(
117 r#" <member>
118 <StackName>{name}</StackName>
119 <StackId>{id}</StackId>
120 <StackStatus>{status}</StackStatus>
121 <CreationTime>{created}</CreationTime>{description_xml}{tags_xml}{params_xml}
122 </member>"#,
123 name = xml_escape(&stack.name),
124 id = xml_escape(&stack.stack_id),
125 status = xml_escape(&stack.status),
126 created = stack.created_at.format("%Y-%m-%dT%H:%M:%SZ"),
127 )
128}
129
130pub fn list_stacks_response(stacks: &[Stack], request_id: &str) -> String {
131 let summaries: String = stacks
132 .iter()
133 .map(|s| {
134 format!(
135 r#" <member>
136 <StackName>{name}</StackName>
137 <StackId>{id}</StackId>
138 <StackStatus>{status}</StackStatus>
139 <CreationTime>{created}</CreationTime>
140 </member>"#,
141 name = xml_escape(&s.name),
142 id = xml_escape(&s.stack_id),
143 status = xml_escape(&s.status),
144 created = s.created_at.format("%Y-%m-%dT%H:%M:%SZ"),
145 )
146 })
147 .collect::<Vec<_>>()
148 .join("\n");
149
150 format!(
151 r#"<ListStacksResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
152 <ListStacksResult>
153 <StackSummaries>
154{summaries}
155 </StackSummaries>
156 </ListStacksResult>
157 <ResponseMetadata>
158 <RequestId>{request_id}</RequestId>
159 </ResponseMetadata>
160</ListStacksResponse>"#,
161 request_id = xml_escape(request_id),
162 )
163}
164
165pub fn list_stack_resources_response(resources: &[StackResource], request_id: &str) -> String {
166 let summaries: String = resources
167 .iter()
168 .map(stack_resource_summary_xml)
169 .collect::<Vec<_>>()
170 .join("\n");
171
172 format!(
173 r#"<ListStackResourcesResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
174 <ListStackResourcesResult>
175 <StackResourceSummaries>
176{summaries}
177 </StackResourceSummaries>
178 </ListStackResourcesResult>
179 <ResponseMetadata>
180 <RequestId>{request_id}</RequestId>
181 </ResponseMetadata>
182</ListStackResourcesResponse>"#,
183 request_id = xml_escape(request_id),
184 )
185}
186
187fn stack_resource_summary_xml(resource: &StackResource) -> String {
188 format!(
189 r#" <member>
190 <LogicalResourceId>{logical_id}</LogicalResourceId>
191 <PhysicalResourceId>{physical_id}</PhysicalResourceId>
192 <ResourceType>{resource_type}</ResourceType>
193 <ResourceStatus>{status}</ResourceStatus>
194 </member>"#,
195 logical_id = xml_escape(&resource.logical_id),
196 physical_id = xml_escape(&resource.physical_id),
197 resource_type = xml_escape(&resource.resource_type),
198 status = xml_escape(&resource.status),
199 )
200}
201
202pub fn describe_stack_resources_response(
203 resources: &[StackResource],
204 stack_name: &str,
205 request_id: &str,
206) -> String {
207 let members: String = resources
208 .iter()
209 .map(|r| {
210 format!(
211 r#" <member>
212 <StackName>{stack_name}</StackName>
213 <LogicalResourceId>{logical_id}</LogicalResourceId>
214 <PhysicalResourceId>{physical_id}</PhysicalResourceId>
215 <ResourceType>{resource_type}</ResourceType>
216 <ResourceStatus>{status}</ResourceStatus>
217 </member>"#,
218 stack_name = xml_escape(stack_name),
219 logical_id = xml_escape(&r.logical_id),
220 physical_id = xml_escape(&r.physical_id),
221 resource_type = xml_escape(&r.resource_type),
222 status = xml_escape(&r.status),
223 )
224 })
225 .collect::<Vec<_>>()
226 .join("\n");
227
228 format!(
229 r#"<DescribeStackResourcesResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
230 <DescribeStackResourcesResult>
231 <StackResources>
232{members}
233 </StackResources>
234 </DescribeStackResourcesResult>
235 <ResponseMetadata>
236 <RequestId>{request_id}</RequestId>
237 </ResponseMetadata>
238</DescribeStackResourcesResponse>"#,
239 request_id = xml_escape(request_id),
240 )
241}
242
243pub fn get_template_response(template_body: &str, request_id: &str) -> String {
244 format!(
245 r#"<GetTemplateResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
246 <GetTemplateResult>
247 <TemplateBody>{template_body}</TemplateBody>
248 </GetTemplateResult>
249 <ResponseMetadata>
250 <RequestId>{request_id}</RequestId>
251 </ResponseMetadata>
252</GetTemplateResponse>"#,
253 template_body = xml_escape(template_body),
254 request_id = xml_escape(request_id),
255 )
256}