1use std::error::Error;
4
5use hcl_edit::{
6 structure::{Body, Structure},
7 visit_mut::VisitMut,
8};
9
10use crate::parser::Field;
11
12struct HclDeleter {
13 fields: Vec<Field>,
14 current_index: usize,
15 current: Option<Field>,
16 error: Option<Box<dyn Error>>,
17}
18
19impl HclDeleter {
20 fn new(fields: Vec<Field>) -> Self {
21 let current = fields.first().cloned();
22 HclDeleter {
23 fields,
24 current_index: 0,
25 current,
26 error: None,
27 }
28 }
29
30 fn next_field(&mut self) {
31 self.current_index += 1;
32 self.current = self.fields.get(self.current_index).cloned();
33 }
34
35 fn previous_field(&mut self) {
36 self.current_index -= 1;
37 self.current = self.fields.get(self.current_index).cloned();
38 }
39
40 fn should_remove(&self) -> bool {
41 self.current_index >= self.fields.len() - 1
42 }
43}
44
45impl VisitMut for HclDeleter {
46 fn visit_body_mut(&mut self, node: &mut Body) {
47 if let Some(current) = self.current.clone() {
48 let mut matching_attr_keys = Vec::new();
49 let mut matching_block_idents = Vec::new();
50 for item in node.iter() {
51 match item {
52 Structure::Attribute(attr) => {
53 if attr.key.as_str() == current.name {
54 matching_attr_keys.push(attr.key.to_string());
55 }
56 }
57 Structure::Block(block) => {
58 if block.ident.as_str() == current.name {
59 if current.labels.is_empty() {
60 matching_block_idents.push(block.ident.to_string());
61 } else {
62 for filter_label in ¤t.labels {
63 for block_label in &block.labels {
64 if block_label.as_str() == filter_label {
65 matching_block_idents.push(block.ident.to_string());
66 }
67 }
68 }
69 }
70 }
71 }
72 }
73 }
74
75 for key in matching_attr_keys {
76 if self.should_remove() {
77 node.remove_attribute(&key);
78 } else {
79 self.next_field();
80 self.visit_attr_mut(node.get_attribute_mut(&key).unwrap());
82 self.previous_field();
83 }
84 }
85
86 for ident in matching_block_idents {
87 if self.should_remove() {
88 node.remove_blocks(&ident);
89 } else {
90 for block in node.get_blocks_mut(&ident) {
91 self.next_field();
92 self.visit_block_mut(block);
93 self.previous_field();
94 }
95 }
96 }
97 }
98 }
99
100 fn visit_object_mut(&mut self, node: &mut hcl_edit::expr::Object) {
101 if let Some(current) = self.current.clone() {
102 let mut matches = Vec::new();
103 for (key, _) in node.iter() {
104 if let Some(id) = key.as_ident() {
106 if id.as_str() == current.name {
107 matches.push(key.clone());
108 }
109 }
110 if let Some(expr) = key.as_expr() {
112 if let Some(expr) = expr.as_str() {
113 if expr == current.name {
114 matches.push(key.clone());
115 }
116 }
117 }
118 }
119
120 for key in matches {
121 if self.should_remove() {
122 node.remove(&key);
123 } else if let Some(val) = node.get_mut(&key) {
124 self.next_field();
127 self.visit_object_value_mut(val);
128 self.previous_field();
129 } else {
130 unreachable!();
133 }
134 }
135 }
136 }
137}
138
139pub fn delete(fields: Vec<Field>, body: &mut Body) -> Result<(), Box<dyn Error>> {
141 let mut visitor = HclDeleter::new(fields);
142 visitor.visit_body_mut(body);
143 if let Some(err) = visitor.error {
144 return Err(err);
145 }
146 Ok(())
147}