Skip to main content

fakecloud_cloudformation/
xml_responses.rs

1use crate::state::{Stack, StackResource};
2
3fn xml_escape(s: &str) -> String {
4    s.replace('&', "&")
5        .replace('<', "&lt;")
6        .replace('>', "&gt;")
7        .replace('"', "&quot;")
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}