1use hcl_edit::{
4 expr::Expression,
5 structure::{Attribute, Body, Structure},
6 visit_mut::VisitMut,
7 Decorate, Decorated, Ident,
8};
9
10use crate::parser::Field;
11
12struct HclEditor<'a> {
13 fields: Vec<Field>,
14 current_index: usize,
15 current: Option<Field>,
16 value: &'a Expression,
17}
18
19impl<'a> HclEditor<'a> {
20 fn new(fields: Vec<Field>, value: &'a Expression) -> Self {
21 let current = fields.first().cloned();
22 HclEditor {
23 fields,
24 current_index: 0,
25 current,
26 value,
27 }
28 }
29
30 fn current_field(&self) -> Option<Field> {
31 self.fields.get(self.current_index).cloned()
32 }
33
34 fn next_field(&mut self) {
35 self.current_index += 1;
36 self.current = self.current_field();
37 }
38
39 fn previous_field(&mut self) {
40 self.current_index -= 1;
41 self.current = self.current_field();
42 }
43
44 fn should_edit(&self) -> bool {
45 self.current_index >= self.fields.len() - 1
46 }
47}
48
49impl VisitMut for HclEditor<'_> {
50 fn visit_body_mut(&mut self, node: &mut Body) {
51 if let Some(current) = self.current.clone() {
52 let mut matching_attr_keys = Vec::new();
53 let mut matching_block_idents = Vec::new();
54 let mut decor = None;
56 for item in node.iter() {
57 match item {
58 Structure::Attribute(attr) => {
59 decor = Some(attr.decor().clone());
61 if attr.key.as_str() == current.name {
62 matching_attr_keys.push(attr.key.to_string());
63 }
64 }
65 Structure::Block(block) => {
66 if block.ident.as_str() == current.name {
67 if current.labels.is_empty() {
68 matching_block_idents.push(block.ident.to_string());
69 } else {
70 for filter_label in ¤t.labels {
71 for block_label in &block.labels {
72 if block_label.as_str() == filter_label {
73 matching_block_idents.push(block.ident.to_string());
74 }
75 }
76 }
77 }
78 }
79 }
80 }
81 }
82
83 for key in matching_attr_keys {
84 self.next_field();
85 self.visit_attr_mut(node.get_attribute_mut(&key).unwrap());
86 self.previous_field();
87 }
88
89 for ident in matching_block_idents {
90 for block in node.get_blocks_mut(&ident) {
91 self.next_field();
92 self.visit_body_mut(&mut block.body);
93 self.previous_field();
94 }
95 }
96
97 if self.should_edit() {
98 let key = Decorated::new(Ident::new(current.name));
99 let decor = decor.unwrap_or_default();
101 let attr = Attribute::new(key, self.value.clone()).decorated(decor);
102 node.insert(node.len(), attr);
103 }
104 }
105 }
106
107 fn visit_attr_mut(&mut self, mut node: hcl_edit::structure::AttributeMut) {
108 if self.should_edit() {
109 let value = node.value_mut();
110 *value = self.value.clone();
111 } else {
112 self.next_field();
113 self.visit_expr_mut(node.value_mut());
114 self.previous_field();
115 }
116 }
117}
118
119pub fn write(fields: Vec<Field>, body: &mut Body, value: &Expression) {
122 let mut visitor = HclEditor::new(fields, value);
123 visitor.visit_body_mut(body);
124}