hcl/expr/template_expr.rs
1use crate::{Error, Identifier, Result};
2use serde::Deserialize;
3use std::fmt;
4use std::str::FromStr;
5
6/// A template expression embeds a program written in the template sub-language as an expression.
7///
8/// This type wraps the raw template string representation. Refer to the documentation of the
9/// [`template`][`crate::template`] module if you need to parse and further evaluate the raw
10/// template.
11#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
12pub enum TemplateExpr {
13 /// A quoted template expression is delimited by quote characters (`"`) and defines a template
14 /// as a single-line expression with escape characters. The raw template string may contain
15 /// escape sequences.
16 QuotedString(String),
17 /// A heredoc template expression is introduced by a `<<` sequence and defines a template via a
18 /// multi-line sequence terminated by a user-chosen delimiter. The raw template string in the
19 /// heredoc may contain escape sequences.
20 Heredoc(Heredoc),
21}
22
23impl TemplateExpr {
24 /// Returns the template as a `&str`.
25 pub(crate) fn as_str(&self) -> &str {
26 match self {
27 TemplateExpr::QuotedString(s) => s,
28 TemplateExpr::Heredoc(heredoc) => &heredoc.template,
29 }
30 }
31}
32
33impl From<&str> for TemplateExpr {
34 fn from(s: &str) -> Self {
35 TemplateExpr::QuotedString(s.to_owned())
36 }
37}
38
39impl From<String> for TemplateExpr {
40 fn from(string: String) -> Self {
41 TemplateExpr::QuotedString(string)
42 }
43}
44
45impl From<Heredoc> for TemplateExpr {
46 fn from(heredoc: Heredoc) -> Self {
47 TemplateExpr::Heredoc(heredoc)
48 }
49}
50
51impl fmt::Display for TemplateExpr {
52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 f.write_str(self.as_str())
54 }
55}
56
57/// A heredoc template expression is introduced by a `<<` sequence and defines a template via a
58/// multi-line sequence terminated by a user-chosen delimiter.
59#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
60pub struct Heredoc {
61 /// The delimiter identifier that denotes the heredoc start and end.
62 pub delimiter: Identifier,
63 /// The raw template contained in the heredoc.
64 pub template: String,
65 /// The heredoc strip mode.
66 pub strip: HeredocStripMode,
67}
68
69impl Heredoc {
70 /// Creates a new `Heredoc` with the provided delimiter and template body.
71 pub fn new<T>(delimiter: Identifier, template: T) -> Heredoc
72 where
73 T: Into<String>,
74 {
75 Heredoc {
76 delimiter,
77 template: template.into(),
78 strip: HeredocStripMode::default(),
79 }
80 }
81
82 /// Sets the heredoc strip mode to use on the template.
83 pub fn with_strip_mode(mut self, strip: HeredocStripMode) -> Heredoc {
84 self.strip = strip;
85 self
86 }
87}
88
89/// The strip behaviour for the template contained in the heredoc.
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
91pub enum HeredocStripMode {
92 /// Do not strip leading whitespace.
93 #[default]
94 None,
95 /// Any literal string at the start of each line is analyzed to find the minimum number
96 /// of leading spaces, and then that number of prefix spaces is removed from all line-leading
97 /// literal strings. The final closing marker may also have an arbitrary number of spaces
98 /// preceding it on its line.
99 Indent,
100}
101
102impl HeredocStripMode {
103 /// Returns the string representation of the heredoc strip mode. This is the part before the
104 /// delimiter identifier.
105 pub fn as_str(&self) -> &'static str {
106 match self {
107 HeredocStripMode::None => "<<",
108 HeredocStripMode::Indent => "<<-",
109 }
110 }
111}
112
113impl FromStr for HeredocStripMode {
114 type Err = Error;
115
116 fn from_str(s: &str) -> Result<Self> {
117 match s {
118 "<<" => Ok(HeredocStripMode::None),
119 "<<-" => Ok(HeredocStripMode::Indent),
120 _ => Err(Error::new(format!("invalid heredoc strip mode: `{s}`"))),
121 }
122 }
123}