Skip to main content

stack_deploy/
change_set.rs

1use aws_sdk_cloudformation::operation::describe_change_set::DescribeChangeSetOutput;
2use aws_sdk_cloudformation::types::{
3    ResourceChange, ResourceChangeDetail, ResourceTargetDefinition,
4};
5use std::fmt::Display;
6use std::io::{self, Write};
7
8struct IndentWriter<'a> {
9    writer: &'a mut dyn Write,
10    level: usize,
11    indent_str: &'static str,
12}
13
14impl<'a> IndentWriter<'a> {
15    fn new(writer: &'a mut dyn Write) -> Self {
16        IndentWriter {
17            writer,
18            level: 0,
19            indent_str: "  ",
20        }
21    }
22
23    fn indent(&mut self) -> IndentWriter<'_> {
24        IndentWriter {
25            writer: self.writer,
26            level: self.level + 1,
27            indent_str: self.indent_str,
28        }
29    }
30
31    fn write_line<T: Display>(&mut self, value: T) -> io::Result<()> {
32        for _ in 0..self.level {
33            write!(self.writer, "{}", self.indent_str)?;
34        }
35        writeln!(self.writer, "{value}")
36    }
37}
38
39pub fn print_change_set_output(output: &DescribeChangeSetOutput) {
40    let mut stdout = io::stdout();
41    let mut writer = IndentWriter::new(&mut stdout);
42    writer.write_line("=== Change Set Details ===").unwrap();
43    print_basic_details(output, &mut writer);
44    print_changes(output, &mut writer);
45}
46
47fn print_basic_details(output: &DescribeChangeSetOutput, writer: &mut IndentWriter<'_>) {
48    if let Some(name) = &output.change_set_name {
49        writer
50            .write_line(format!("Change Set Name: {name}"))
51            .unwrap();
52    }
53    if let Some(id) = &output.change_set_id {
54        writer.write_line(format!("Change Set ID: {id}")).unwrap();
55    }
56    if let Some(stack_name) = &output.stack_name {
57        writer
58            .write_line(format!("Stack Name: {stack_name}"))
59            .unwrap();
60    }
61    if let Some(execution_status) = &output.execution_status {
62        writer
63            .write_line(format!("Execution Status: {execution_status}"))
64            .unwrap();
65    }
66    if let Some(status) = &output.status {
67        writer.write_line(format!("Status: {status:?}")).unwrap();
68    }
69    if let Some(status_reason) = &output.status_reason {
70        writer
71            .write_line(format!("Status Reason: {status_reason}"))
72            .unwrap();
73    }
74    if let Some(creation_time) = &output.creation_time {
75        writer
76            .write_line(format!("Creation Time: {creation_time}"))
77            .unwrap();
78    }
79    if let Some(description) = &output.description {
80        writer
81            .write_line(format!("Description: {description}"))
82            .unwrap();
83    }
84}
85
86fn print_changes(output: &DescribeChangeSetOutput, writer: &mut IndentWriter<'_>) {
87    if let Some(changes) = &output.changes {
88        for change in changes {
89            if let Some(resource_change) = &change.resource_change {
90                print_resource_change(resource_change, writer);
91            }
92        }
93    }
94}
95
96fn print_resource_change(resource_change: &ResourceChange, writer: &mut IndentWriter<'_>) {
97    let logical_resource_id = resource_change.logical_resource_id.as_deref().unwrap();
98    let resource_type = resource_change.resource_type.as_deref().unwrap();
99    let action = resource_change.action.as_ref().unwrap();
100
101    let mut indented_writer = writer.indent();
102    indented_writer
103        .write_line(format!("{logical_resource_id}:"))
104        .unwrap();
105
106    let mut detail_writer = indented_writer.indent();
107    detail_writer
108        .write_line(format!("Resource Type: {resource_type}"))
109        .unwrap();
110    detail_writer
111        .write_line(format!("Action: {action:?}"))
112        .unwrap();
113
114    if let Some(replacement) = &resource_change.replacement {
115        detail_writer
116            .write_line(format!("Replacement: {replacement:?}"))
117            .unwrap();
118    }
119
120    if let Some(details) = &resource_change.details
121        && !details.is_empty()
122    {
123        detail_writer.write_line("Details:").unwrap();
124        for detail in details {
125            print_resource_change_detail(detail, &mut detail_writer);
126        }
127    }
128}
129
130fn print_resource_change_detail(detail: &ResourceChangeDetail, writer: &mut IndentWriter<'_>) {
131    let mut indented_writer = writer.indent();
132    indented_writer.write_line("Detail:").unwrap();
133
134    let mut inner_writer = indented_writer.indent();
135    if let Some(target) = &detail.target {
136        print_target(target, &mut inner_writer);
137    }
138    if let Some(change_source) = &detail.change_source {
139        inner_writer
140            .write_line(format!("Change Source: {change_source:?}"))
141            .unwrap();
142    }
143    if let Some(causing_entity) = &detail.causing_entity {
144        inner_writer
145            .write_line(format!("Causing Entity: {causing_entity}"))
146            .unwrap();
147    }
148    if let Some(evaluation) = &detail.evaluation {
149        inner_writer
150            .write_line(format!("Evaluation: {evaluation:?}"))
151            .unwrap();
152    }
153}
154
155fn print_target(target: &ResourceTargetDefinition, writer: &mut IndentWriter<'_>) {
156    writer.write_line("Target:").unwrap();
157
158    let mut indented_writer = writer.indent();
159    if let Some(attribute) = &target.attribute {
160        indented_writer
161            .write_line(format!("Attribute: {attribute:?}"))
162            .unwrap();
163    }
164    print_optional_field("Name", &target.name, &mut indented_writer);
165    if let Some(requires_recreation) = &target.requires_recreation {
166        indented_writer
167            .write_line(format!("Requires Recreation: {requires_recreation}"))
168            .unwrap();
169    }
170    print_optional_field("Path", &target.path, &mut indented_writer);
171    print_optional_field("Before Value", &target.before_value, &mut indented_writer);
172    print_optional_field("After Value", &target.after_value, &mut indented_writer);
173    print_optional_field(
174        "Attribute Change Type",
175        &target.attribute_change_type,
176        &mut indented_writer,
177    );
178}
179
180fn print_optional_field<T: Display>(
181    field_name: &str,
182    field: &Option<T>,
183    writer: &mut IndentWriter<'_>,
184) {
185    if let Some(value) = field {
186        writer.write_line(format!("{field_name}: {value}")).unwrap();
187    }
188}