hcl_edit/template/
mod.rs

1//! Types to represent the HCL template sub-language.
2
3#[cfg(test)]
4mod tests;
5
6use crate::encode::{Encode, EncodeState};
7use crate::expr::Expression;
8use crate::util::{dedent_by, min_leading_whitespace};
9use crate::{parser, Decor, Decorate, Decorated, Ident, RawString, Spanned};
10use std::fmt;
11use std::ops::{Deref, DerefMut, Range};
12use std::str::FromStr;
13
14// Re-exported for convenience.
15#[doc(inline)]
16pub use hcl_primitives::template::Strip;
17
18/// An owning iterator over the elements of a `Template`.
19///
20/// Values of this type are created by the [`into_iter`] method on [`Template`] (provided by the
21/// [`IntoIterator`] trait). See its documentation for more.
22///
23/// [`into_iter`]: IntoIterator::into_iter
24/// [`IntoIterator`]: core::iter::IntoIterator
25pub type IntoIter = Box<dyn Iterator<Item = Element>>;
26
27/// An iterator over the elements of a `Template`.
28///
29/// Values of this type are created by the [`iter`] method on [`Template`]. See its documentation
30/// for more.
31///
32/// [`iter`]: Template::iter
33pub type Iter<'a> = Box<dyn Iterator<Item = &'a Element> + 'a>;
34
35/// A mutable iterator over the elements of a `Template`.
36///
37/// Values of this type are created by the [`iter_mut`] method on [`Template`]. See its
38/// documentation for more.
39///
40/// [`iter_mut`]: Template::iter_mut
41pub type IterMut<'a> = Box<dyn Iterator<Item = &'a mut Element> + 'a>;
42
43/// A type representing the HCL template sub-languange in the context of a quoted string literal.
44///
45/// A template behaves like an expression that always returns a string value. The different
46/// elements of the template are evaluated and combined into a single string to return.
47#[derive(Debug, Clone, Eq, Default)]
48pub struct StringTemplate {
49    template: Template,
50    decor: Decor,
51}
52
53impl StringTemplate {
54    /// Constructs a new, empty `StringTemplate`.
55    #[inline]
56    pub fn new() -> Self {
57        StringTemplate::default()
58    }
59
60    /// Constructs a new, empty `StringTemplate` with at least the specified capacity.
61    #[inline]
62    pub fn with_capacity(capacity: usize) -> Self {
63        StringTemplate {
64            template: Template::with_capacity(capacity),
65            ..Default::default()
66        }
67    }
68
69    pub(crate) fn despan(&mut self, input: &str) {
70        self.decor.despan(input);
71        self.template.despan(input);
72    }
73}
74
75impl From<Vec<Element>> for StringTemplate {
76    fn from(elements: Vec<Element>) -> Self {
77        StringTemplate {
78            template: Template::from(elements),
79            decor: Decor::default(),
80        }
81    }
82}
83
84impl PartialEq for StringTemplate {
85    fn eq(&self, other: &Self) -> bool {
86        self.template == other.template
87    }
88}
89
90impl Deref for StringTemplate {
91    type Target = Template;
92
93    #[inline]
94    fn deref(&self) -> &Self::Target {
95        &self.template
96    }
97}
98
99impl DerefMut for StringTemplate {
100    #[inline]
101    fn deref_mut(&mut self) -> &mut Self::Target {
102        &mut self.template
103    }
104}
105
106impl<T> Extend<T> for StringTemplate
107where
108    T: Into<Element>,
109{
110    fn extend<I>(&mut self, iterable: I)
111    where
112        I: IntoIterator<Item = T>,
113    {
114        self.template.extend(iterable);
115    }
116}
117
118impl<T> FromIterator<T> for StringTemplate
119where
120    T: Into<Element>,
121{
122    fn from_iter<I>(iterable: I) -> Self
123    where
124        I: IntoIterator<Item = T>,
125    {
126        Template::from_iter(iterable).into()
127    }
128}
129
130impl IntoIterator for StringTemplate {
131    type Item = Element;
132    type IntoIter = IntoIter;
133
134    fn into_iter(self) -> Self::IntoIter {
135        self.template.into_iter()
136    }
137}
138
139impl<'a> IntoIterator for &'a StringTemplate {
140    type Item = &'a Element;
141    type IntoIter = Iter<'a>;
142
143    fn into_iter(self) -> Self::IntoIter {
144        self.template.iter()
145    }
146}
147
148impl<'a> IntoIterator for &'a mut StringTemplate {
149    type Item = &'a mut Element;
150    type IntoIter = IterMut<'a>;
151
152    fn into_iter(self) -> Self::IntoIter {
153        self.template.iter_mut()
154    }
155}
156
157/// A heredoc template is introduced by a `<<` sequence and defines a template via a multi-line
158/// sequence terminated by a user-chosen delimiter.
159#[derive(Debug, Clone, Eq)]
160pub struct HeredocTemplate {
161    /// The delimiter identifier that denotes the heredoc start and end.
162    pub delimiter: Ident,
163    /// The raw template contained in the heredoc.
164    pub template: Template,
165
166    indent: Option<usize>,
167    trailing: RawString,
168    decor: Decor,
169    span: Option<Range<usize>>,
170}
171
172impl HeredocTemplate {
173    /// Creates a new `HeredocTemplate` for a delimiter and a template.
174    pub fn new(delimiter: Ident, template: Template) -> HeredocTemplate {
175        HeredocTemplate {
176            delimiter,
177            template,
178            indent: None,
179            trailing: RawString::default(),
180            decor: Decor::default(),
181            span: None,
182        }
183    }
184
185    /// Return the heredoc's indent, if there is any.
186    pub fn indent(&self) -> Option<usize> {
187        self.indent
188    }
189
190    /// Set the heredoc's indent.
191    pub fn set_indent(&mut self, indent: usize) {
192        self.indent = Some(indent);
193    }
194
195    /// Return a reference to the raw trailing decor before the heredoc's closing delimiter.
196    pub fn trailing(&self) -> &RawString {
197        &self.trailing
198    }
199
200    /// Set the raw trailing decor before the heredoc's closing delimiter.
201    pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
202        self.trailing = trailing.into();
203    }
204
205    /// Dedent the heredoc template.
206    ///
207    /// This will set the heredoc's indent to the number of leading
208    /// spaces that were stripped off of template string literals, if any.
209    pub fn dedent(&mut self) {
210        let stripped_indent = self.template.dedent();
211        self.indent = stripped_indent;
212    }
213
214    pub(crate) fn despan(&mut self, input: &str) {
215        self.decor.despan(input);
216        self.template.despan(input);
217        self.trailing.despan(input);
218    }
219}
220
221impl PartialEq for HeredocTemplate {
222    fn eq(&self, other: &Self) -> bool {
223        self.delimiter == other.delimiter
224            && self.template == other.template
225            && self.indent == other.indent
226            && self.trailing == other.trailing
227    }
228}
229
230/// The main type to represent the HCL template sub-languange.
231///
232/// A template behaves like an expression that always returns a string value. The different
233/// elements of the template are evaluated and combined into a single string to return.
234#[derive(Debug, Clone, Eq, Default)]
235pub struct Template {
236    elements: Vec<Element>,
237    span: Option<Range<usize>>,
238}
239
240impl Template {
241    /// Constructs a new, empty `Template`.
242    #[inline]
243    pub fn new() -> Self {
244        Template::default()
245    }
246
247    /// Constructs a new, empty `Template` with at least the specified capacity.
248    #[inline]
249    pub fn with_capacity(capacity: usize) -> Self {
250        Template {
251            elements: Vec::with_capacity(capacity),
252            ..Default::default()
253        }
254    }
255
256    /// Returns `true` if the template contains no elements.
257    #[inline]
258    pub fn is_empty(&self) -> bool {
259        self.elements.is_empty()
260    }
261
262    /// Returns the number of elements in the template, also referred to as its 'length'.
263    #[inline]
264    pub fn len(&self) -> usize {
265        self.elements.len()
266    }
267
268    /// Clears the template, removing all elements.
269    #[inline]
270    pub fn clear(&mut self) {
271        self.elements.clear();
272    }
273
274    /// Returns a reference to the element at the given index, or `None` if the index is out of
275    /// bounds.
276    #[inline]
277    pub fn get(&self, index: usize) -> Option<&Element> {
278        self.elements.get(index)
279    }
280
281    /// Returns a mutable reference to the element at the given index, or `None` if the index is
282    /// out of bounds.
283    #[inline]
284    pub fn get_mut(&mut self, index: usize) -> Option<&mut Element> {
285        self.elements.get_mut(index)
286    }
287
288    /// Inserts an element at position `index` within the template, shifting all elements after it
289    /// to the right.
290    ///
291    /// # Panics
292    ///
293    /// Panics if `index > len`.
294    #[inline]
295    pub fn insert(&mut self, index: usize, element: impl Into<Element>) {
296        self.elements.insert(index, element.into());
297    }
298
299    /// Appends an element to the back of the template.
300    ///
301    /// # Panics
302    ///
303    /// Panics if the new capacity exceeds `isize::MAX` bytes.
304    #[inline]
305    pub fn push(&mut self, element: impl Into<Element>) {
306        self.elements.push(element.into());
307    }
308
309    /// Removes the last element from the template and returns it, or [`None`] if it is empty.
310    #[inline]
311    pub fn pop(&mut self) -> Option<Element> {
312        self.elements.pop()
313    }
314
315    /// Removes and returns the element at position `index` within the template, shifting all
316    /// elements after it to the left.
317    ///
318    /// Like `Vec::remove`, the element is removed by shifting all of the elements that follow it,
319    /// preserving their relative order. **This perturbs the index of all of those elements!**
320    ///
321    /// # Panics
322    ///
323    /// Panics if `index` is out of bounds.
324    #[inline]
325    pub fn remove(&mut self, index: usize) -> Element {
326        self.elements.remove(index)
327    }
328
329    /// An iterator visiting all template elements in insertion order. The iterator element type
330    /// is `&'a Element`.
331    #[inline]
332    pub fn iter(&self) -> Iter<'_> {
333        Box::new(self.elements.iter())
334    }
335
336    /// An iterator visiting all template elements in insertion order, with mutable references to
337    /// the values. The iterator element type is `&'a mut Element`.
338    #[inline]
339    pub fn iter_mut(&mut self) -> IterMut<'_> {
340        Box::new(self.elements.iter_mut())
341    }
342
343    /// If the template consists of a single `Element`, returns a reference to it, otherwise
344    /// `None`.
345    ///
346    /// # Example
347    ///
348    /// ```
349    /// use hcl_edit::template::{Element, Template};
350    ///
351    /// let mut template = Template::new();
352    ///
353    /// template.push("one");
354    ///
355    /// assert_eq!(template.as_single_element(), Some(&Element::from("one")));
356    ///
357    /// template.push("two");
358    ///
359    /// assert_eq!(template.as_single_element(), None);
360    /// ```
361    pub fn as_single_element(&self) -> Option<&Element> {
362        match self.len() {
363            1 => self.get(0),
364            _ => None,
365        }
366    }
367
368    /// If the template consists of a single `Element`, returns a mutable reference to it,
369    /// otherwise `None`.
370    ///
371    /// # Example
372    ///
373    /// ```
374    /// use hcl_edit::template::{Element, Template};
375    ///
376    /// let mut template = Template::new();
377    ///
378    /// template.push("one");
379    ///
380    /// if let Some(element) = template.as_single_element_mut() {
381    ///     *element = Element::from("two");
382    /// }
383    ///
384    /// template.push("three");
385    ///
386    /// assert_eq!(template.as_single_element(), None);
387    /// assert_eq!(template, Template::from_iter(["two", "three"]));
388    /// ```
389    pub fn as_single_element_mut(&mut self) -> Option<&mut Element> {
390        match self.len() {
391            1 => self.get_mut(0),
392            _ => None,
393        }
394    }
395
396    pub(crate) fn despan(&mut self, input: &str) {
397        for element in &mut self.elements {
398            element.despan(input);
399        }
400    }
401
402    /// Dedents string literals in the template, returning the maximum indent that was stripped,
403    /// if any.
404    pub(crate) fn dedent(&mut self) -> Option<usize> {
405        let mut indent: Option<usize> = None;
406        let mut skip_first_line = false;
407
408        for element in &self.elements {
409            if let Element::Literal(literal) = element {
410                if let Some(leading_ws) = min_leading_whitespace(literal, skip_first_line) {
411                    indent = Some(indent.map_or(leading_ws, |indent| indent.min(leading_ws)));
412                }
413                skip_first_line = !literal.ends_with('\n');
414            } else if !skip_first_line {
415                // Directive or interpolation at line start always mean that no indent can be
416                // stripped.
417                return None;
418            }
419        }
420
421        if let Some(indent) = indent {
422            skip_first_line = false;
423
424            for element in &mut self.elements {
425                if let Element::Literal(literal) = element {
426                    let dedented = dedent_by(literal, indent, skip_first_line);
427                    *literal.as_mut() = dedented.into();
428                    skip_first_line = !literal.ends_with('\n');
429                } else if !skip_first_line {
430                    skip_first_line = true;
431                }
432            }
433        }
434
435        indent
436    }
437}
438
439impl PartialEq for Template {
440    fn eq(&self, other: &Self) -> bool {
441        self.elements == other.elements
442    }
443}
444
445impl fmt::Display for Template {
446    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447        let mut state = EncodeState::new(f);
448        self.encode(&mut state)
449    }
450}
451
452impl FromStr for Template {
453    type Err = parser::Error;
454
455    fn from_str(s: &str) -> Result<Self, Self::Err> {
456        parser::parse_template(s)
457    }
458}
459
460impl From<Vec<Element>> for Template {
461    fn from(elements: Vec<Element>) -> Self {
462        Template {
463            elements,
464            ..Default::default()
465        }
466    }
467}
468
469impl From<Template> for StringTemplate {
470    fn from(template: Template) -> Self {
471        StringTemplate {
472            template,
473            ..Default::default()
474        }
475    }
476}
477
478impl From<StringTemplate> for Template {
479    fn from(template: StringTemplate) -> Self {
480        template.template
481    }
482}
483
484impl<T> Extend<T> for Template
485where
486    T: Into<Element>,
487{
488    fn extend<I>(&mut self, iterable: I)
489    where
490        I: IntoIterator<Item = T>,
491    {
492        let iter = iterable.into_iter();
493        let reserve = if self.is_empty() {
494            iter.size_hint().0
495        } else {
496            (iter.size_hint().0 + 1) / 2
497        };
498        self.elements.reserve(reserve);
499        iter.for_each(|v| self.push(v));
500    }
501}
502
503impl<T> FromIterator<T> for Template
504where
505    T: Into<Element>,
506{
507    fn from_iter<I>(iterable: I) -> Self
508    where
509        I: IntoIterator<Item = T>,
510    {
511        let iter = iterable.into_iter();
512        let lower = iter.size_hint().0;
513        let mut template = Template::with_capacity(lower);
514        template.extend(iter);
515        template
516    }
517}
518
519impl IntoIterator for Template {
520    type Item = Element;
521    type IntoIter = IntoIter;
522
523    fn into_iter(self) -> Self::IntoIter {
524        Box::new(self.elements.into_iter())
525    }
526}
527
528impl<'a> IntoIterator for &'a Template {
529    type Item = &'a Element;
530    type IntoIter = Iter<'a>;
531
532    fn into_iter(self) -> Self::IntoIter {
533        self.iter()
534    }
535}
536
537impl<'a> IntoIterator for &'a mut Template {
538    type Item = &'a mut Element;
539    type IntoIter = IterMut<'a>;
540
541    fn into_iter(self) -> Self::IntoIter {
542        self.iter_mut()
543    }
544}
545
546/// An element of an HCL template.
547#[derive(Debug, Clone, PartialEq, Eq)]
548pub enum Element {
549    /// A literal sequence of characters to include in the resulting string.
550    Literal(Spanned<String>),
551    /// An interpolation sequence that evaluates an expression (written in the expression
552    /// sub-language), and converts the result to a string value.
553    Interpolation(Interpolation),
554    /// An `if` or `for` directive that allows for conditional template evaluation.
555    Directive(Directive),
556}
557
558impl Element {
559    /// Returns `true` if the element represents a literal string.
560    pub fn is_literal(&self) -> bool {
561        self.as_literal().is_some()
562    }
563
564    /// If the `Element` is a literal string, returns a reference to it, otherwise `None`.
565    pub fn as_literal(&self) -> Option<&Spanned<String>> {
566        match self {
567            Element::Literal(value) => Some(value),
568            Element::Interpolation(_) | Element::Directive(_) => None,
569        }
570    }
571
572    /// Returns `true` if the element represents an interpolation.
573    pub fn is_interpolation(&self) -> bool {
574        self.as_interpolation().is_some()
575    }
576
577    /// If the `Element` is an interpolation, returns a reference to it, otherwise `None`.
578    pub fn as_interpolation(&self) -> Option<&Interpolation> {
579        match self {
580            Element::Interpolation(value) => Some(value),
581            Element::Literal(_) | Element::Directive(_) => None,
582        }
583    }
584
585    /// Returns `true` if the element represents a directive.
586    pub fn is_directive(&self) -> bool {
587        self.as_directive().is_some()
588    }
589
590    /// If the `Element` is a directive, returns a reference to it, otherwise `None`.
591    pub fn as_directive(&self) -> Option<&Directive> {
592        match self {
593            Element::Directive(value) => Some(value),
594            Element::Literal(_) | Element::Interpolation(_) => None,
595        }
596    }
597
598    pub(crate) fn despan(&mut self, input: &str) {
599        match self {
600            Element::Literal(_) => {}
601            Element::Interpolation(interp) => interp.despan(input),
602            Element::Directive(dir) => dir.despan(input),
603        }
604    }
605}
606
607impl From<&str> for Element {
608    fn from(value: &str) -> Self {
609        Element::from(value.to_string())
610    }
611}
612
613impl From<String> for Element {
614    fn from(value: String) -> Self {
615        Element::from(Spanned::new(value))
616    }
617}
618
619impl From<Spanned<String>> for Element {
620    fn from(value: Spanned<String>) -> Self {
621        Element::Literal(value)
622    }
623}
624
625impl From<Interpolation> for Element {
626    fn from(value: Interpolation) -> Self {
627        Element::Interpolation(value)
628    }
629}
630
631impl From<Directive> for Element {
632    fn from(value: Directive) -> Self {
633        Element::Directive(value)
634    }
635}
636
637/// An interpolation sequence evaluates an expression (written in the expression sub-language),
638/// converts the result to a string value, and replaces itself with the resulting string.
639#[derive(Debug, Clone, Eq)]
640pub struct Interpolation {
641    /// The interpolated expression.
642    pub expr: Expression,
643    /// The whitespace strip behaviour to use on the template elements preceeding and following
644    /// after this interpolation sequence.
645    pub strip: Strip,
646
647    span: Option<Range<usize>>,
648}
649
650impl Interpolation {
651    /// Creates a new `Interpolation` from an expression.
652    pub fn new(expr: impl Into<Expression>) -> Interpolation {
653        Interpolation {
654            expr: expr.into(),
655            strip: Strip::default(),
656            span: None,
657        }
658    }
659
660    pub(crate) fn despan(&mut self, input: &str) {
661        self.expr.despan(input);
662    }
663}
664
665impl PartialEq for Interpolation {
666    fn eq(&self, other: &Self) -> bool {
667        self.expr == other.expr && self.strip == other.strip
668    }
669}
670
671/// A template directive that allows for conditional template evaluation.
672#[derive(Debug, Clone, PartialEq, Eq)]
673pub enum Directive {
674    /// Represents a template `if` directive.
675    If(IfDirective),
676    /// Represents a template `for` directive.
677    For(ForDirective),
678}
679
680impl Directive {
681    pub(crate) fn despan(&mut self, input: &str) {
682        match self {
683            Directive::If(dir) => dir.despan(input),
684            Directive::For(dir) => dir.despan(input),
685        }
686    }
687}
688
689impl From<IfDirective> for Directive {
690    fn from(value: IfDirective) -> Self {
691        Directive::If(value)
692    }
693}
694
695impl From<ForDirective> for Directive {
696    fn from(value: ForDirective) -> Self {
697        Directive::For(value)
698    }
699}
700
701/// The template `if` directive is the template equivalent of the conditional expression, allowing
702/// selection of one of two sub-templates based on the condition result.
703#[derive(Debug, Clone, Eq)]
704pub struct IfDirective {
705    /// The `if` sub-expression within the directive.
706    pub if_expr: IfTemplateExpr,
707    /// The `else` sub-expression within the directive. This is `None` if there is no `else`
708    /// branch in which case the result string will be empty.
709    pub else_expr: Option<ElseTemplateExpr>,
710    /// The `endif` sub-expression within the directive.
711    pub endif_expr: EndifTemplateExpr,
712
713    span: Option<Range<usize>>,
714}
715
716impl IfDirective {
717    /// Creates a new `IfDirective` from the parts for the `if`, `else` and `endif`
718    /// sub-expressions.
719    pub fn new(
720        if_expr: IfTemplateExpr,
721        else_expr: Option<ElseTemplateExpr>,
722        endif_expr: EndifTemplateExpr,
723    ) -> IfDirective {
724        IfDirective {
725            if_expr,
726            else_expr,
727            endif_expr,
728            span: None,
729        }
730    }
731
732    pub(crate) fn despan(&mut self, input: &str) {
733        self.if_expr.despan(input);
734
735        if let Some(else_expr) = &mut self.else_expr {
736            else_expr.despan(input);
737        }
738
739        self.endif_expr.despan(input);
740    }
741}
742
743impl PartialEq for IfDirective {
744    fn eq(&self, other: &Self) -> bool {
745        self.if_expr == other.if_expr
746            && self.else_expr == other.else_expr
747            && self.endif_expr == other.endif_expr
748    }
749}
750
751/// A type representing the `%{ if cond_expr }` sub-expression and the template that follows after
752/// it within an [`IfDirective`].
753#[derive(Debug, Clone, PartialEq, Eq)]
754pub struct IfTemplateExpr {
755    /// The condition expression.
756    pub cond_expr: Expression,
757    /// The template that is included in the result string if the conditional expression evaluates
758    /// to `true`.
759    pub template: Template,
760    /// The whitespace strip behaviour to use on the template elements preceeding and following
761    /// after the `if` expression.
762    pub strip: Strip,
763
764    preamble: RawString,
765}
766
767impl IfTemplateExpr {
768    /// Creates a new `IfTemplateExpr` for a condition expression and a template.
769    pub fn new(cond_expr: impl Into<Expression>, template: Template) -> IfTemplateExpr {
770        IfTemplateExpr {
771            preamble: RawString::default(),
772            cond_expr: cond_expr.into(),
773            template,
774            strip: Strip::default(),
775        }
776    }
777
778    /// Return a reference to the raw leading decor after the `if`'s opening `{`.
779    pub fn preamble(&self) -> &RawString {
780        &self.preamble
781    }
782
783    /// Set the raw leading decor after the `if`'s opening `{`.
784    pub fn set_preamble(&mut self, preamble: impl Into<RawString>) {
785        self.preamble = preamble.into();
786    }
787
788    pub(crate) fn despan(&mut self, input: &str) {
789        self.preamble.despan(input);
790        self.cond_expr.despan(input);
791        self.template.despan(input);
792    }
793}
794
795/// A type representing the `%{ else }` sub-expression and the template that follows after it
796/// within an [`IfDirective`].
797#[derive(Debug, Clone, PartialEq, Eq)]
798pub struct ElseTemplateExpr {
799    /// The template that is included in the result string if the `if` branch's conditional
800    /// expression evaluates to `false`.
801    pub template: Template,
802    /// The whitespace strip behaviour to use on the template elements preceeding and following
803    /// after the `else` expression.
804    pub strip: Strip,
805
806    preamble: RawString,
807    trailing: RawString,
808}
809
810impl ElseTemplateExpr {
811    /// Creates a new `ElseTemplateExpr` for a template.
812    pub fn new(template: Template) -> ElseTemplateExpr {
813        ElseTemplateExpr {
814            preamble: RawString::default(),
815            trailing: RawString::default(),
816            template,
817            strip: Strip::default(),
818        }
819    }
820
821    /// Return a reference to the raw leading decor after the `else`'s opening `{`.
822    pub fn preamble(&self) -> &RawString {
823        &self.preamble
824    }
825
826    /// Set the raw leading decor after the `else`'s opening `{`.
827    pub fn set_preamble(&mut self, preamble: impl Into<RawString>) {
828        self.preamble = preamble.into();
829    }
830
831    /// Return a reference to the raw trailing decor before the `else`'s closing `}`.
832    pub fn trailing(&self) -> &RawString {
833        &self.trailing
834    }
835
836    /// Set the raw trailing decor before the `else`'s closing `}`.
837    pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
838        self.trailing = trailing.into();
839    }
840
841    pub(crate) fn despan(&mut self, input: &str) {
842        self.preamble.despan(input);
843        self.template.despan(input);
844        self.trailing.despan(input);
845    }
846}
847
848/// A type representing the `%{ endif }` sub-expression within an [`IfDirective`].
849#[derive(Debug, Clone, Default, PartialEq, Eq)]
850pub struct EndifTemplateExpr {
851    /// The whitespace strip behaviour to use on the template elements preceeding and following
852    /// after the `endif` marker.
853    pub strip: Strip,
854
855    preamble: RawString,
856    trailing: RawString,
857}
858
859impl EndifTemplateExpr {
860    /// Creates a new `EndifTemplateExpr`.
861    pub fn new() -> EndifTemplateExpr {
862        EndifTemplateExpr::default()
863    }
864
865    /// Return a reference to the raw leading decor after the `endif`'s opening `{`.
866    pub fn preamble(&self) -> &RawString {
867        &self.preamble
868    }
869
870    /// Set the raw leading decor after the `endif`'s opening `{`.
871    pub fn set_preamble(&mut self, preamble: impl Into<RawString>) {
872        self.preamble = preamble.into();
873    }
874
875    /// Return a reference to the raw trailing decor before the `endif`'s closing `}`.
876    pub fn trailing(&self) -> &RawString {
877        &self.trailing
878    }
879
880    /// Set the raw trailing decor before the `endif`'s closing `}`.
881    pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
882        self.trailing = trailing.into();
883    }
884
885    pub(crate) fn despan(&mut self, input: &str) {
886        self.preamble.despan(input);
887        self.trailing.despan(input);
888    }
889}
890
891/// The template `for` directive is the template equivalent of the for expression, producing zero
892/// or more copies of its sub-template based on the elements of a collection.
893#[derive(Debug, Clone, Eq)]
894pub struct ForDirective {
895    /// The `for` sub-expression within the directive.
896    pub for_expr: ForTemplateExpr,
897    /// The `endfor` sub-expression within the directive.
898    pub endfor_expr: EndforTemplateExpr,
899
900    span: Option<Range<usize>>,
901}
902
903impl ForDirective {
904    /// Creates a new `ForDirective` from the parts for the `for` and `endfor` sub-expressions.
905    pub fn new(for_expr: ForTemplateExpr, endfor_expr: EndforTemplateExpr) -> ForDirective {
906        ForDirective {
907            for_expr,
908            endfor_expr,
909            span: None,
910        }
911    }
912
913    pub(crate) fn despan(&mut self, input: &str) {
914        self.for_expr.despan(input);
915        self.endfor_expr.despan(input);
916    }
917}
918
919impl PartialEq for ForDirective {
920    fn eq(&self, other: &Self) -> bool {
921        self.for_expr == other.for_expr && self.endfor_expr == other.endfor_expr
922    }
923}
924
925/// A type representing the `%{ for key_var, value_var in collection_expr }` sub-expression and
926/// the template that follows after it within a [`ForDirective`].
927#[derive(Debug, Clone, PartialEq, Eq)]
928pub struct ForTemplateExpr {
929    /// Optional iterator key variable identifier.
930    pub key_var: Option<Decorated<Ident>>,
931    /// The iterator value variable identifier.
932    pub value_var: Decorated<Ident>,
933    /// The expression that produces the list or object of elements to iterate over.
934    pub collection_expr: Expression,
935    /// The template that is included in the result string for each loop iteration.
936    pub template: Template,
937    /// The whitespace strip behaviour to use on the template elements preceeding and following
938    /// after the `for` expression.
939    pub strip: Strip,
940
941    preamble: RawString,
942}
943
944impl ForTemplateExpr {
945    /// Creates a new `ForTemplateExpr` from an optional key variable, value variable, collection
946    /// expression and template.
947    pub fn new(
948        key_var: Option<impl Into<Decorated<Ident>>>,
949        value_var: impl Into<Decorated<Ident>>,
950        collection_expr: impl Into<Expression>,
951        template: Template,
952    ) -> ForTemplateExpr {
953        ForTemplateExpr {
954            preamble: RawString::default(),
955            key_var: key_var.map(Into::into),
956            value_var: value_var.into(),
957            collection_expr: collection_expr.into(),
958            template,
959            strip: Strip::default(),
960        }
961    }
962
963    /// Return a reference to the raw leading decor after the `for`'s opening `{`.
964    pub fn preamble(&self) -> &RawString {
965        &self.preamble
966    }
967
968    /// Set the raw leading decor after the `for`'s opening `{`.
969    pub fn set_preamble(&mut self, preamble: impl Into<RawString>) {
970        self.preamble = preamble.into();
971    }
972
973    pub(crate) fn despan(&mut self, input: &str) {
974        self.preamble.despan(input);
975
976        if let Some(key_var) = &mut self.key_var {
977            key_var.decor_mut().despan(input);
978        }
979
980        self.value_var.decor_mut().despan(input);
981        self.collection_expr.despan(input);
982        self.template.despan(input);
983    }
984}
985
986/// A type representing the `%{ endfor }` sub-expression within a [`ForDirective`].
987#[derive(Debug, Clone, Default, PartialEq, Eq)]
988pub struct EndforTemplateExpr {
989    /// The whitespace strip behaviour to use on the template elements preceeding and following
990    /// after the `endfor` marker.
991    pub strip: Strip,
992
993    preamble: RawString,
994    trailing: RawString,
995}
996
997impl EndforTemplateExpr {
998    /// Creates a new `EndforTemplateExpr`.
999    pub fn new() -> EndforTemplateExpr {
1000        EndforTemplateExpr::default()
1001    }
1002
1003    /// Return a reference to the raw leading decor after the `endfor`'s opening `{`.
1004    pub fn preamble(&self) -> &RawString {
1005        &self.preamble
1006    }
1007
1008    /// Set the raw leading decor after the `endfor`'s opening `{`.
1009    pub fn set_preamble(&mut self, preamble: impl Into<RawString>) {
1010        self.preamble = preamble.into();
1011    }
1012
1013    /// Return a reference to the raw trailing decor before the `endfor`'s closing `}`.
1014    pub fn trailing(&self) -> &RawString {
1015        &self.trailing
1016    }
1017
1018    /// Set the raw trailing decor before the `endfor`'s closing `}`.
1019    pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
1020        self.trailing = trailing.into();
1021    }
1022
1023    pub(crate) fn despan(&mut self, input: &str) {
1024        self.preamble.despan(input);
1025        self.trailing.despan(input);
1026    }
1027}
1028
1029decorate_impl! { StringTemplate, HeredocTemplate }
1030
1031span_impl! {
1032    StringTemplate, HeredocTemplate, Template,
1033    Interpolation, IfDirective, ForDirective
1034}
1035
1036forward_span_impl! {
1037    Element => { Literal, Interpolation, Directive },
1038    Directive => { If, For }
1039}