hcl_edit/
visit_mut.rs

1//! Mutable HCL language item traversal.
2//!
3//! Each method of the [`VisitMut`] trait is a hook that can be overridden to customize the
4//! behavior when mutating the corresponding type of language item. By default, every method
5//! recursively visits the substructure of the AST by invoking the right visitor method of each of
6//! its fields.
7//!
8//! The API is modeled after
9//! [`syn::visit_mut`](https://docs.rs/syn/latest/syn/visit_mut/index.html). For an alternative
10//! that works on shared borrows, see [`hcl_edit::visit`](crate::visit).
11//!
12//! # Examples
13//!
14//! Namespace all referenced variables with `var.`:
15//!
16//! ```
17//! # use std::error::Error;
18//! #
19//! # fn main() -> Result<(), Box<dyn Error>> {
20//! use hcl_edit::expr::{Expression, Traversal, TraversalOperator};
21//! use hcl_edit::prelude::*;
22//! use hcl_edit::structure::Body;
23//! use hcl_edit::visit_mut::{visit_expr_mut, VisitMut};
24//! use hcl_edit::{Decorated, Ident};
25//! use std::str::FromStr;
26//!
27//! struct VariableNamespacer {
28//!     namespace: Decorated<Ident>,
29//! }
30//!
31//! impl VisitMut for VariableNamespacer {
32//!     fn visit_expr_mut(&mut self, expr: & mut Expression) {
33//!         if let Expression::Variable(var) = expr {
34//!             // Remove the decor and apply it to the new expression.
35//!             let decor = std::mem::take(var.decor_mut());
36//!
37//!             let namespace = Expression::Variable(self.namespace.clone());
38//!             let operators = vec![Decorated::new(TraversalOperator::GetAttr(var.clone()))];
39//!             let traversal = Traversal::new(namespace, operators);
40//!
41//!             *expr = Expression::from(traversal).decorated(decor);
42//!         } else {
43//!             // Recurse further down the AST.
44//!             visit_expr_mut(self, expr);
45//!         }
46//!     }
47//! }
48//!
49//! let input = r#"
50//!     // A service definition.
51//!     service {
52//!         fullname        = "${namespace}/${name}"
53//!         health_endpoint = "${base_url}/health"
54//!     }
55//! "#;
56//!
57//! let mut body = input.parse::<Body>()?;
58//!
59//! let mut visitor = VariableNamespacer {
60//!     namespace: Decorated::new(Ident::new("var")),
61//! };
62//!
63//! visitor.visit_body_mut(&mut body);
64//!
65//! let expected = r#"
66//!     // A service definition.
67//!     service {
68//!         fullname        = "${var.namespace}/${var.name}"
69//!         health_endpoint = "${var.base_url}/health"
70//!     }
71//! "#;
72//!
73//! assert_eq!(body.to_string(), expected);
74//! #   Ok(())
75//! # }
76//! ```
77
78#![allow(missing_docs)]
79
80use crate::expr::{
81    Array, BinaryOp, BinaryOperator, Conditional, Expression, ForCond, ForExpr, ForIntro, FuncArgs,
82    FuncCall, FuncName, Null, Object, ObjectKeyMut, ObjectValue, Parenthesis, Splat, Traversal,
83    TraversalOperator, UnaryOp, UnaryOperator,
84};
85use crate::structure::{AttributeMut, Block, BlockLabel, Body, StructureMut};
86use crate::template::{
87    Directive, Element, ElseTemplateExpr, EndforTemplateExpr, EndifTemplateExpr, ForDirective,
88    ForTemplateExpr, HeredocTemplate, IfDirective, IfTemplateExpr, Interpolation, StringTemplate,
89    Template,
90};
91use crate::{Decorated, Formatted, Ident, Number, Spanned};
92
93macro_rules! empty_visit_mut_methods {
94    ($($name: ident => $t: ty),+ $(,)?) => {
95        $(
96            fn $name(&mut self, node: &mut $t) {
97                let _ = node;
98            }
99        )*
100    };
101}
102
103macro_rules! visit_mut_methods {
104    ($($name: ident => $t: ty),+ $(,)?) => {
105        $(
106            fn $name(&mut self, node: &mut $t) {
107                $name(self, node);
108            }
109        )*
110    };
111}
112
113/// Traversal to walk a mutable borrow of an HCL language item.
114///
115/// See the [module documentation](crate::visit_mut) for details.
116pub trait VisitMut {
117    empty_visit_mut_methods! {
118        visit_ident_mut => Decorated<Ident>,
119        visit_null_mut => Decorated<Null>,
120        visit_bool_mut => Decorated<bool>,
121        visit_u64_mut => Decorated<u64>,
122        visit_number_mut => Formatted<Number>,
123        visit_string_mut => Decorated<String>,
124        visit_splat_mut => Decorated<Splat>,
125        visit_literal_mut => Spanned<String>,
126        visit_unary_operator_mut => Spanned<UnaryOperator>,
127        visit_binary_operator_mut => Spanned<BinaryOperator>,
128        visit_endif_template_expr_mut => EndifTemplateExpr,
129        visit_endfor_template_expr_mut => EndforTemplateExpr,
130    }
131
132    visit_mut_methods! {
133        visit_body_mut => Body,
134        visit_block_mut => Block,
135        visit_block_label_mut => BlockLabel,
136        visit_expr_mut => Expression,
137        visit_array_mut => Array,
138        visit_object_mut => Object,
139        visit_object_value_mut => ObjectValue,
140        visit_parenthesis_mut => Parenthesis,
141        visit_conditional_mut => Conditional,
142        visit_unary_op_mut => UnaryOp,
143        visit_binary_op_mut => BinaryOp,
144        visit_traversal_mut => Traversal,
145        visit_traversal_operator_mut => TraversalOperator,
146        visit_func_call_mut => FuncCall,
147        visit_func_name_mut => FuncName,
148        visit_func_args_mut => FuncArgs,
149        visit_for_expr_mut => ForExpr,
150        visit_for_intro_mut => ForIntro,
151        visit_for_cond_mut => ForCond,
152        visit_string_template_mut => StringTemplate,
153        visit_heredoc_template_mut => HeredocTemplate,
154        visit_template_mut => Template,
155        visit_element_mut => Element,
156        visit_interpolation_mut => Interpolation,
157        visit_directive_mut => Directive,
158        visit_if_directive_mut => IfDirective,
159        visit_for_directive_mut => ForDirective,
160        visit_if_template_expr_mut => IfTemplateExpr,
161        visit_else_template_expr_mut => ElseTemplateExpr,
162        visit_for_template_expr_mut => ForTemplateExpr,
163    }
164
165    fn visit_structure_mut(&mut self, node: StructureMut) {
166        visit_structure_mut(self, node);
167    }
168
169    fn visit_attr_mut(&mut self, node: AttributeMut) {
170        visit_attr_mut(self, node);
171    }
172
173    fn visit_object_key_mut(&mut self, node: ObjectKeyMut) {
174        let _ = node;
175    }
176
177    fn visit_object_item_mut(&mut self, key: ObjectKeyMut, value: &mut ObjectValue) {
178        visit_object_item_mut(self, key, value);
179    }
180}
181
182pub fn visit_body_mut<V>(v: &mut V, node: &mut Body)
183where
184    V: VisitMut + ?Sized,
185{
186    for structure in &mut *node {
187        v.visit_structure_mut(structure);
188    }
189}
190
191pub fn visit_structure_mut<V>(v: &mut V, mut node: StructureMut)
192where
193    V: VisitMut + ?Sized,
194{
195    if let Some(attr) = node.as_attribute_mut() {
196        v.visit_attr_mut(attr);
197    } else if let Some(block) = node.as_block_mut() {
198        v.visit_block_mut(block);
199    }
200}
201
202pub fn visit_attr_mut<V>(v: &mut V, mut node: AttributeMut)
203where
204    V: VisitMut + ?Sized,
205{
206    v.visit_expr_mut(node.value_mut());
207}
208
209pub fn visit_block_mut<V>(v: &mut V, node: &mut Block)
210where
211    V: VisitMut + ?Sized,
212{
213    v.visit_ident_mut(&mut node.ident);
214    for label in &mut node.labels {
215        v.visit_block_label_mut(label);
216    }
217    v.visit_body_mut(&mut node.body);
218}
219
220pub fn visit_block_label_mut<V>(v: &mut V, node: &mut BlockLabel)
221where
222    V: VisitMut + ?Sized,
223{
224    match node {
225        BlockLabel::String(string) => v.visit_string_mut(string),
226        BlockLabel::Ident(ident) => v.visit_ident_mut(ident),
227    }
228}
229
230pub fn visit_expr_mut<V>(v: &mut V, node: &mut Expression)
231where
232    V: VisitMut + ?Sized,
233{
234    match node {
235        Expression::Null(null) => v.visit_null_mut(null),
236        Expression::Bool(b) => v.visit_bool_mut(b),
237        Expression::Number(number) => v.visit_number_mut(number),
238        Expression::String(string) => v.visit_string_mut(string),
239        Expression::Array(array) => v.visit_array_mut(array),
240        Expression::Object(object) => v.visit_object_mut(object),
241        Expression::StringTemplate(template) => v.visit_string_template_mut(template),
242        Expression::HeredocTemplate(template) => v.visit_heredoc_template_mut(template),
243        Expression::Parenthesis(parens) => v.visit_parenthesis_mut(parens),
244        Expression::Variable(var) => v.visit_ident_mut(var),
245        Expression::ForExpr(for_expr) => v.visit_for_expr_mut(for_expr),
246        Expression::Conditional(conditional) => v.visit_conditional_mut(conditional),
247        Expression::FuncCall(func_call) => v.visit_func_call_mut(func_call),
248        Expression::UnaryOp(unary_op) => v.visit_unary_op_mut(unary_op),
249        Expression::BinaryOp(binary_op) => v.visit_binary_op_mut(binary_op),
250        Expression::Traversal(traversal) => v.visit_traversal_mut(traversal),
251    }
252}
253
254pub fn visit_array_mut<V>(v: &mut V, node: &mut Array)
255where
256    V: VisitMut + ?Sized,
257{
258    for expr in &mut *node {
259        v.visit_expr_mut(expr);
260    }
261}
262
263pub fn visit_object_mut<V>(v: &mut V, node: &mut Object)
264where
265    V: VisitMut + ?Sized,
266{
267    for (key, value) in &mut *node {
268        v.visit_object_item_mut(key, value);
269    }
270}
271
272pub fn visit_object_item_mut<V>(v: &mut V, key: ObjectKeyMut, value: &mut ObjectValue)
273where
274    V: VisitMut + ?Sized,
275{
276    v.visit_object_key_mut(key);
277    v.visit_object_value_mut(value);
278}
279
280pub fn visit_object_value_mut<V>(v: &mut V, node: &mut ObjectValue)
281where
282    V: VisitMut + ?Sized,
283{
284    v.visit_expr_mut(node.expr_mut());
285}
286
287pub fn visit_parenthesis_mut<V>(v: &mut V, node: &mut Parenthesis)
288where
289    V: VisitMut + ?Sized,
290{
291    v.visit_expr_mut(node.inner_mut());
292}
293
294pub fn visit_conditional_mut<V>(v: &mut V, node: &mut Conditional)
295where
296    V: VisitMut + ?Sized,
297{
298    v.visit_expr_mut(&mut node.cond_expr);
299    v.visit_expr_mut(&mut node.true_expr);
300    v.visit_expr_mut(&mut node.false_expr);
301}
302
303pub fn visit_unary_op_mut<V>(v: &mut V, node: &mut UnaryOp)
304where
305    V: VisitMut + ?Sized,
306{
307    v.visit_unary_operator_mut(&mut node.operator);
308    v.visit_expr_mut(&mut node.expr);
309}
310
311pub fn visit_binary_op_mut<V>(v: &mut V, node: &mut BinaryOp)
312where
313    V: VisitMut + ?Sized,
314{
315    v.visit_expr_mut(&mut node.lhs_expr);
316    v.visit_binary_operator_mut(&mut node.operator);
317    v.visit_expr_mut(&mut node.rhs_expr);
318}
319
320pub fn visit_traversal_mut<V>(v: &mut V, node: &mut Traversal)
321where
322    V: VisitMut + ?Sized,
323{
324    v.visit_expr_mut(&mut node.expr);
325    for operator in &mut node.operators {
326        v.visit_traversal_operator_mut(operator);
327    }
328}
329
330pub fn visit_traversal_operator_mut<V>(v: &mut V, node: &mut TraversalOperator)
331where
332    V: VisitMut + ?Sized,
333{
334    match node {
335        TraversalOperator::AttrSplat(splat) | TraversalOperator::FullSplat(splat) => {
336            v.visit_splat_mut(splat);
337        }
338        TraversalOperator::GetAttr(ident) => v.visit_ident_mut(ident),
339        TraversalOperator::Index(expr) => v.visit_expr_mut(expr),
340        TraversalOperator::LegacyIndex(u) => v.visit_u64_mut(u),
341    }
342}
343
344pub fn visit_func_call_mut<V>(v: &mut V, node: &mut FuncCall)
345where
346    V: VisitMut + ?Sized,
347{
348    v.visit_func_name_mut(&mut node.name);
349    v.visit_func_args_mut(&mut node.args);
350}
351
352pub fn visit_func_name_mut<V>(v: &mut V, node: &mut FuncName)
353where
354    V: VisitMut + ?Sized,
355{
356    for component in &mut node.namespace {
357        v.visit_ident_mut(component);
358    }
359    v.visit_ident_mut(&mut node.name);
360}
361
362pub fn visit_func_args_mut<V>(v: &mut V, node: &mut FuncArgs)
363where
364    V: VisitMut + ?Sized,
365{
366    for arg in &mut *node {
367        v.visit_expr_mut(arg);
368    }
369}
370
371pub fn visit_for_expr_mut<V>(v: &mut V, node: &mut ForExpr)
372where
373    V: VisitMut + ?Sized,
374{
375    v.visit_for_intro_mut(&mut node.intro);
376    if let Some(key_expr) = &mut node.key_expr {
377        v.visit_expr_mut(key_expr);
378    }
379    v.visit_expr_mut(&mut node.value_expr);
380    if let Some(cond) = &mut node.cond {
381        v.visit_for_cond_mut(cond);
382    }
383}
384
385pub fn visit_for_intro_mut<V>(v: &mut V, node: &mut ForIntro)
386where
387    V: VisitMut + ?Sized,
388{
389    if let Some(key_var) = &mut node.key_var {
390        v.visit_ident_mut(key_var);
391    }
392    v.visit_ident_mut(&mut node.value_var);
393    v.visit_expr_mut(&mut node.collection_expr);
394}
395
396pub fn visit_for_cond_mut<V>(v: &mut V, node: &mut ForCond)
397where
398    V: VisitMut + ?Sized,
399{
400    v.visit_expr_mut(&mut node.expr);
401}
402
403pub fn visit_string_template_mut<V>(v: &mut V, node: &mut StringTemplate)
404where
405    V: VisitMut + ?Sized,
406{
407    for element in &mut *node {
408        v.visit_element_mut(element);
409    }
410}
411
412pub fn visit_heredoc_template_mut<V>(v: &mut V, node: &mut HeredocTemplate)
413where
414    V: VisitMut + ?Sized,
415{
416    v.visit_template_mut(&mut node.template);
417}
418
419pub fn visit_template_mut<V>(v: &mut V, node: &mut Template)
420where
421    V: VisitMut + ?Sized,
422{
423    for element in &mut *node {
424        v.visit_element_mut(element);
425    }
426}
427
428pub fn visit_element_mut<V>(v: &mut V, node: &mut Element)
429where
430    V: VisitMut + ?Sized,
431{
432    match node {
433        Element::Literal(literal) => v.visit_literal_mut(literal),
434        Element::Interpolation(interpolation) => v.visit_interpolation_mut(interpolation),
435        Element::Directive(directive) => v.visit_directive_mut(directive),
436    }
437}
438
439pub fn visit_interpolation_mut<V>(v: &mut V, node: &mut Interpolation)
440where
441    V: VisitMut + ?Sized,
442{
443    v.visit_expr_mut(&mut node.expr);
444}
445
446pub fn visit_directive_mut<V>(v: &mut V, node: &mut Directive)
447where
448    V: VisitMut + ?Sized,
449{
450    match node {
451        Directive::If(if_directive) => v.visit_if_directive_mut(if_directive),
452        Directive::For(for_directive) => v.visit_for_directive_mut(for_directive),
453    }
454}
455
456pub fn visit_if_directive_mut<V>(v: &mut V, node: &mut IfDirective)
457where
458    V: VisitMut + ?Sized,
459{
460    v.visit_if_template_expr_mut(&mut node.if_expr);
461    if let Some(else_template_expr) = &mut node.else_expr {
462        v.visit_else_template_expr_mut(else_template_expr);
463    }
464    v.visit_endif_template_expr_mut(&mut node.endif_expr);
465}
466
467pub fn visit_for_directive_mut<V>(v: &mut V, node: &mut ForDirective)
468where
469    V: VisitMut + ?Sized,
470{
471    v.visit_for_template_expr_mut(&mut node.for_expr);
472    v.visit_endfor_template_expr_mut(&mut node.endfor_expr);
473}
474
475pub fn visit_if_template_expr_mut<V>(v: &mut V, node: &mut IfTemplateExpr)
476where
477    V: VisitMut + ?Sized,
478{
479    v.visit_expr_mut(&mut node.cond_expr);
480    v.visit_template_mut(&mut node.template);
481}
482
483pub fn visit_else_template_expr_mut<V>(v: &mut V, node: &mut ElseTemplateExpr)
484where
485    V: VisitMut + ?Sized,
486{
487    v.visit_template_mut(&mut node.template);
488}
489
490pub fn visit_for_template_expr_mut<V>(v: &mut V, node: &mut ForTemplateExpr)
491where
492    V: VisitMut + ?Sized,
493{
494    if let Some(key_var) = &mut node.key_var {
495        v.visit_ident_mut(key_var);
496    }
497    v.visit_ident_mut(&mut node.value_var);
498    v.visit_template_mut(&mut node.template);
499}