vrl/compiler/expression/
literal.rs

1use std::{borrow::Cow, convert::TryFrom, fmt, sync::Arc};
2
3use crate::diagnostic::{DiagnosticMessage, Label, Note, Urls};
4use crate::value::{Value, ValueRegex};
5use bytes::Bytes;
6use chrono::{DateTime, SecondsFormat, Utc};
7use ordered_float::NotNan;
8use regex::Regex;
9
10use crate::compiler::{
11    Context, Expression, Span, TypeDef,
12    expression::Resolved,
13    state::{TypeInfo, TypeState},
14};
15
16#[derive(Debug, Clone, PartialEq)]
17pub enum Literal {
18    String(Bytes),
19    Integer(i64),
20    Float(NotNan<f64>),
21    Boolean(bool),
22    Regex(ValueRegex),
23    Timestamp(DateTime<Utc>),
24    Null,
25}
26
27impl Literal {
28    /// Get a `Value` type stored in the literal.
29    ///
30    /// This differs from `Expression::as_value` insofar as this *always*
31    /// returns a `Value`, whereas `as_value` returns `Option<Value>` which, in
32    /// the case of `Literal` means it always returns `Some(Value)`, requiring
33    /// an extra `unwrap()`.
34    pub fn to_value(&self) -> Value {
35        use Literal::{Boolean, Float, Integer, Null, Regex, String, Timestamp};
36
37        match self {
38            String(v) => Value::Bytes(v.clone()),
39            Integer(v) => Value::Integer(*v),
40            Float(v) => Value::Float(*v),
41            Boolean(v) => Value::Boolean(*v),
42            Regex(v) => Value::Regex(v.clone()),
43            Timestamp(v) => Value::Timestamp(*v),
44            Null => Value::Null,
45        }
46    }
47}
48
49impl Expression for Literal {
50    fn resolve(&self, _: &mut Context) -> Resolved {
51        Ok(self.to_value())
52    }
53
54    fn resolve_constant(&self, _state: &TypeState) -> Option<Value> {
55        Some(self.to_value())
56    }
57
58    fn type_info(&self, state: &TypeState) -> TypeInfo {
59        use Literal::{Boolean, Float, Integer, Null, Regex, String, Timestamp};
60
61        let type_def = match self {
62            String(_) => TypeDef::bytes(),
63            Integer(_) => TypeDef::integer(),
64            Float(_) => TypeDef::float(),
65            Boolean(_) => TypeDef::boolean(),
66            Regex(_) => TypeDef::regex(),
67            Timestamp(_) => TypeDef::timestamp(),
68            Null => TypeDef::null(),
69        };
70
71        TypeInfo::new(state, type_def.infallible())
72    }
73}
74
75impl fmt::Display for Literal {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        use Literal::{Boolean, Float, Integer, Null, Regex, String, Timestamp};
78
79        match self {
80            String(v) => write!(f, r#""{}""#, std::string::String::from_utf8_lossy(v)),
81            Integer(v) => v.fmt(f),
82            Float(v) => v.fmt(f),
83            Boolean(v) => v.fmt(f),
84            Regex(v) => v.fmt(f),
85            Timestamp(v) => write!(f, "t'{}'", v.to_rfc3339_opts(SecondsFormat::AutoSi, true)),
86            Null => f.write_str("null"),
87        }
88    }
89}
90
91// Literal::String -------------------------------------------------------------
92
93impl From<Bytes> for Literal {
94    fn from(v: Bytes) -> Self {
95        Literal::String(v)
96    }
97}
98
99impl From<Cow<'_, str>> for Literal {
100    fn from(v: Cow<'_, str>) -> Self {
101        v.as_ref().into()
102    }
103}
104
105impl From<Vec<u8>> for Literal {
106    fn from(v: Vec<u8>) -> Self {
107        v.as_slice().into()
108    }
109}
110
111impl From<&[u8]> for Literal {
112    fn from(v: &[u8]) -> Self {
113        Literal::String(Bytes::copy_from_slice(v))
114    }
115}
116
117impl From<String> for Literal {
118    fn from(v: String) -> Self {
119        Literal::String(v.into())
120    }
121}
122
123impl From<&str> for Literal {
124    fn from(v: &str) -> Self {
125        Literal::String(Bytes::copy_from_slice(v.as_bytes()))
126    }
127}
128
129// Literal::Integer ------------------------------------------------------------
130
131impl From<i8> for Literal {
132    fn from(v: i8) -> Self {
133        Literal::Integer(i64::from(v))
134    }
135}
136
137impl From<i16> for Literal {
138    fn from(v: i16) -> Self {
139        Literal::Integer(i64::from(v))
140    }
141}
142
143impl From<i32> for Literal {
144    fn from(v: i32) -> Self {
145        Literal::Integer(i64::from(v))
146    }
147}
148
149impl From<i64> for Literal {
150    fn from(v: i64) -> Self {
151        Literal::Integer(v)
152    }
153}
154
155impl From<u16> for Literal {
156    fn from(v: u16) -> Self {
157        Literal::Integer(i64::from(v))
158    }
159}
160
161impl From<u32> for Literal {
162    fn from(v: u32) -> Self {
163        Literal::Integer(i64::from(v))
164    }
165}
166
167impl From<u64> for Literal {
168    fn from(v: u64) -> Self {
169        #[allow(clippy::cast_possible_wrap)]
170        Literal::Integer(v as i64)
171    }
172}
173
174impl From<usize> for Literal {
175    fn from(v: usize) -> Self {
176        #[allow(clippy::cast_possible_wrap)]
177        Literal::Integer(v as i64)
178    }
179}
180
181// Literal::Float --------------------------------------------------------------
182
183impl From<NotNan<f64>> for Literal {
184    fn from(v: NotNan<f64>) -> Self {
185        Literal::Float(v)
186    }
187}
188
189impl TryFrom<f64> for Literal {
190    type Error = Error;
191
192    fn try_from(v: f64) -> Result<Self, Self::Error> {
193        Ok(Literal::Float(NotNan::new(v).map_err(|_| Error {
194            span: Span::default(),
195            variant: ErrorVariant::NanFloat,
196        })?))
197    }
198}
199
200// Literal::Boolean ------------------------------------------------------------
201
202impl From<bool> for Literal {
203    fn from(v: bool) -> Self {
204        Literal::Boolean(v)
205    }
206}
207
208// Literal::Regex --------------------------------------------------------------
209
210impl From<Arc<Regex>> for Literal {
211    fn from(regex: Arc<Regex>) -> Self {
212        Literal::Regex(ValueRegex::new(regex))
213    }
214}
215
216impl From<ValueRegex> for Literal {
217    fn from(regex: ValueRegex) -> Self {
218        Literal::Regex(regex)
219    }
220}
221
222// Literal::Null ---------------------------------------------------------------
223
224impl From<()> for Literal {
225    fn from((): ()) -> Self {
226        Literal::Null
227    }
228}
229
230impl<T: Into<Literal>> From<Option<T>> for Literal {
231    fn from(literal: Option<T>) -> Self {
232        match literal {
233            None => Literal::Null,
234            Some(v) => v.into(),
235        }
236    }
237}
238
239// Literal::Regex --------------------------------------------------------------
240
241impl From<DateTime<Utc>> for Literal {
242    fn from(dt: DateTime<Utc>) -> Self {
243        Literal::Timestamp(dt)
244    }
245}
246
247// -----------------------------------------------------------------------------
248
249#[derive(Debug)]
250pub struct Error {
251    pub(crate) variant: ErrorVariant,
252    span: Span,
253}
254
255#[derive(thiserror::Error, Debug)]
256pub(crate) enum ErrorVariant {
257    #[error("invalid regular expression")]
258    InvalidRegex(#[from] regex::Error),
259
260    #[error("invalid timestamp")]
261    InvalidTimestamp(#[from] chrono::ParseError),
262
263    #[error("float literal can't be NaN")]
264    NanFloat,
265}
266
267impl fmt::Display for Error {
268    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269        write!(f, "{:#}", self.variant)
270    }
271}
272
273impl std::error::Error for Error {
274    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
275        Some(&self.variant)
276    }
277}
278
279impl DiagnosticMessage for Error {
280    fn code(&self) -> usize {
281        use ErrorVariant::{InvalidRegex, InvalidTimestamp, NanFloat};
282
283        match &self.variant {
284            InvalidRegex(..) => 101,
285            InvalidTimestamp(..) => 601,
286            NanFloat => 602,
287        }
288    }
289
290    fn labels(&self) -> Vec<Label> {
291        use ErrorVariant::{InvalidRegex, InvalidTimestamp, NanFloat};
292
293        match &self.variant {
294            InvalidRegex(err) => {
295                let error = err
296                    .to_string()
297                    .lines()
298                    .filter_map(|line| {
299                        if line.trim() == "^" || line == "regex parse error:" {
300                            return None;
301                        }
302
303                        Some(line.trim_start_matches("error: ").trim())
304                    })
305                    .rev()
306                    .collect::<Vec<_>>()
307                    .join(": ");
308
309                vec![Label::primary(
310                    format!("regex parse error: {error}"),
311                    self.span,
312                )]
313            }
314            InvalidTimestamp(err) => vec![Label::primary(
315                format!("invalid timestamp format: {err}"),
316                self.span,
317            )],
318
319            NanFloat => vec![],
320        }
321    }
322
323    fn notes(&self) -> Vec<Note> {
324        use ErrorVariant::{InvalidRegex, InvalidTimestamp, NanFloat};
325
326        match &self.variant {
327            InvalidRegex(_) => vec![Note::SeeDocs(
328                "regular expressions".to_owned(),
329                Urls::expression_docs_url("#regular-expression"),
330            )],
331            InvalidTimestamp(_) => vec![Note::SeeDocs(
332                "timestamps".to_owned(),
333                Urls::expression_docs_url("#timestamp"),
334            )],
335            NanFloat => vec![Note::SeeDocs(
336                "floats".to_owned(),
337                Urls::expression_docs_url("#float"),
338            )],
339        }
340    }
341}
342
343impl From<(Span, regex::Error)> for Error {
344    fn from((span, err): (Span, regex::Error)) -> Self {
345        Self {
346            variant: err.into(),
347            span,
348        }
349    }
350}
351
352impl From<(Span, chrono::ParseError)> for Error {
353    fn from((span, err): (Span, chrono::ParseError)) -> Self {
354        Self {
355            variant: err.into(),
356            span,
357        }
358    }
359}
360
361#[cfg(test)]
362mod tests {
363    use crate::compiler::TypeDef;
364    use crate::{expr, test_type_def};
365
366    test_type_def![
367        bytes {
368            expr: |_| expr!("foo"),
369            want: TypeDef::bytes(),
370        }
371
372        integer {
373            expr: |_| expr!(12),
374            want: TypeDef::integer(),
375        }
376    ];
377}