vector/template/
unconfined.rs1use super::parsing::{parse_template, render_metric_field, render_timestamp};
2use super::*;
3
4impl fmt::Debug for UnconfinedTemplate {
5 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6 f.debug_struct("UnconfinedTemplate")
7 .field("src", &self.src)
8 .field("is_static", &self.is_static)
9 .field("tz_offset", &self.tz_offset)
10 .finish()
11 }
12}
13
14impl TryFrom<&str> for UnconfinedTemplate {
15 type Error = TemplateParseError;
16
17 fn try_from(src: &str) -> Result<Self, Self::Error> {
18 UnconfinedTemplate::try_from(Cow::Borrowed(src))
19 }
20}
21
22impl TryFrom<String> for UnconfinedTemplate {
23 type Error = TemplateParseError;
24
25 fn try_from(src: String) -> Result<Self, Self::Error> {
26 UnconfinedTemplate::try_from(Cow::Owned(src))
27 }
28}
29
30impl TryFrom<PathBuf> for UnconfinedTemplate {
31 type Error = TemplateParseError;
32
33 fn try_from(p: PathBuf) -> Result<Self, Self::Error> {
34 UnconfinedTemplate::try_from(p.to_string_lossy().into_owned())
35 }
36}
37
38impl TryFrom<Cow<'_, str>> for UnconfinedTemplate {
39 type Error = TemplateParseError;
40
41 fn try_from(src: Cow<'_, str>) -> Result<Self, Self::Error> {
42 parse_template(&src).map(|parts| {
43 let is_static =
44 parts.is_empty() || (parts.len() == 1 && matches!(parts[0], Part::Literal(..)));
45
46 let reserve_size = parts
47 .iter()
48 .map(|part| match part {
49 Part::Literal(lit) => lit.len(),
50 Part::Reference(_path) => 1,
51 Part::Strftime(parsed) => parsed.reserve_size(),
52 })
53 .sum();
54
55 UnconfinedTemplate {
56 parts,
57 src: src.into_owned(),
58 is_static,
59 reserve_size,
60 tz_offset: None,
61 }
62 })
63 }
64}
65
66impl From<UnconfinedTemplate> for String {
67 fn from(template: UnconfinedTemplate) -> String {
68 template.src
69 }
70}
71
72impl fmt::Display for UnconfinedTemplate {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 self.src.fmt(f)
75 }
76}
77
78impl ConfigurableString for UnconfinedTemplate {}
80
81impl UnconfinedTemplate {
82 pub const fn with_tz_offset(mut self, tz_offset: Option<FixedOffset>) -> Self {
84 self.tz_offset = tz_offset;
85 self
86 }
87
88 pub fn render<'a>(
90 &self,
91 event: impl Into<EventRef<'a>>,
92 ) -> Result<Bytes, TemplateRenderingError> {
93 self.render_string(event.into()).map(Into::into)
94 }
95
96 pub fn render_string<'a>(
99 &self,
100 event: impl Into<EventRef<'a>>,
101 ) -> Result<String, TemplateRenderingError> {
102 if self.is_static {
103 Ok(self.src.clone())
104 } else {
105 self.render_event(event.into())
106 }
107 }
108
109 fn render_event(&self, event: EventRef<'_>) -> Result<String, TemplateRenderingError> {
110 let mut missing_keys = Vec::new();
111 let mut out = String::with_capacity(self.reserve_size);
112 for part in &self.parts {
113 match part {
114 Part::Literal(lit) => out.push_str(lit),
115 Part::Strftime(items) => {
116 out.push_str(&render_timestamp(items, event, self.tz_offset))
117 }
118 Part::Reference(key) => {
119 out.push_str(
120 &match event {
121 EventRef::Log(log) => log
122 .parse_path_and_get_value(key)
123 .ok()
124 .and_then(|v| v.map(Value::to_string_lossy)),
125 EventRef::Metric(metric) => {
126 render_metric_field(key, metric).map(Cow::Borrowed)
127 }
128 EventRef::Trace(trace) => trace
129 .parse_path_and_get_value(key)
130 .ok()
131 .and_then(|v| v.map(Value::to_string_lossy)),
132 }
133 .unwrap_or_else(|| {
134 missing_keys.push(key.to_owned());
135 Cow::Borrowed("")
136 }),
137 );
138 }
139 }
140 }
141 if missing_keys.is_empty() {
142 Ok(out)
143 } else {
144 Err(TemplateRenderingError::MissingKeys { missing_keys })
145 }
146 }
147
148 pub fn get_fields(&self) -> Option<Vec<String>> {
150 let parts: Vec<_> = self
151 .parts
152 .iter()
153 .filter_map(|part| {
154 if let Part::Reference(r) = part {
155 Some(r.to_owned())
156 } else {
157 None
158 }
159 })
160 .collect();
161 (!parts.is_empty()).then_some(parts)
162 }
163
164 pub fn literal_prefix(&self) -> &str {
170 let bytes = self.src.as_bytes();
171 let mut i = 0;
172 while i < bytes.len() {
173 if bytes[i] == b'{' && bytes.get(i + 1) == Some(&b'{') {
175 break;
176 }
177 if bytes[i] == b'%' {
183 break;
184 }
185 i += 1;
186 }
187 self.src.split_at(i).0
188 }
189
190 pub const fn get_ref(&self) -> &str {
192 self.src.as_str()
193 }
194
195 pub const fn is_empty(&self) -> bool {
197 self.src.is_empty()
198 }
199
200 pub const fn is_dynamic(&self) -> bool {
202 !self.is_static
203 }
204}