vrl/parser/
template_string.rs

1use std::fmt;
2
3use crate::diagnostic::Span;
4
5use super::ast::{Expr, Ident, Literal::RawString, Node, Op, Opcode};
6
7#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, Hash)]
8pub enum StringSegment {
9    Literal(String, Span),
10    Template(String, Span),
11}
12
13impl fmt::Display for StringSegment {
14    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15        match self {
16            StringSegment::Literal(s, _) => write!(f, "{s}"),
17            StringSegment::Template(s, _) => write!(f, "{{{{ {s} }}}}"),
18        }
19    }
20}
21
22#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, Hash)]
23pub struct TemplateString(pub Vec<StringSegment>);
24
25impl TemplateString {
26    /// Rewrites the ast for the template string to be a series of string concatenations
27    #[must_use]
28    pub fn rewrite_to_concatenated_strings(&self) -> Expr {
29        self.0
30            .iter()
31            .map(|node| match node {
32                StringSegment::Literal(s, span) => {
33                    (*span, Expr::Literal(Node::new(*span, RawString(s.clone()))))
34                }
35                StringSegment::Template(s, span) => {
36                    (*span, Expr::Variable(Node::new(*span, Ident::new(s))))
37                }
38            })
39            .reduce(|accum, item| {
40                let (item_span, item) = item;
41                let (accum_span, accum) = accum;
42                let total_span = Span::new(accum_span.start(), item_span.end());
43                (
44                    total_span,
45                    Expr::Op(Node::new(
46                        total_span,
47                        Op(
48                            Box::new(Node::new(accum_span, accum)),
49                            Node::new(item_span, Opcode::Add),
50                            Box::new(Node::new(item_span, item)),
51                        ),
52                    )),
53                )
54            })
55            .map_or_else(
56                || {
57                    Expr::Literal(Node::new(
58                        crate::diagnostic::Span::default(),
59                        RawString(String::new()),
60                    ))
61                },
62                |(_span, expr)| expr,
63            )
64    }
65
66    /// If the template string is just a single literal string return that string
67    /// as we can just represent it in the ast as a single literal, otherwise return
68    /// None as we will need to rewrite it into an expression.
69    #[must_use]
70    pub fn as_literal_string(&self) -> Option<&str> {
71        match self.0.as_slice() {
72            [StringSegment::Literal(s, _)] => Some(s),
73            _ => None,
74        }
75    }
76}
77
78impl fmt::Display for TemplateString {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        for segment in &self.0 {
81            segment.fmt(f)?;
82        }
83
84        Ok(())
85    }
86}