Skip to main content

vrl/compiler/value/
convert.rs

1use std::borrow::Cow;
2
3use crate::value::{Value, ValueRegex, kind::Collection};
4use bytes::Bytes;
5use chrono::{DateTime, Utc};
6
7use crate::compiler::{
8    Expression,
9    expression::{Container, Expr, Variant},
10    value::{Kind, ObjectMap, ValueError},
11};
12
13/// Create a boxed [`Expression`] trait object from a given `Value`.
14///
15/// Supports the same format as the [`value`](`crate::value::value`) macro.
16#[macro_export]
17macro_rules! expr {
18    ($($v:tt)*) => {{
19        let value = $crate::value!($($v)*);
20        $crate::compiler::value::VrlValueConvert::into_expression(value)
21    }};
22}
23
24#[allow(clippy::module_name_repetitions, clippy::missing_errors_doc)]
25pub trait VrlValueConvert: Sized {
26    /// Convert a given [`Value`] into a [`Expression`] trait object.
27    fn into_expression(self) -> Box<dyn Expression>;
28
29    fn try_integer(self) -> Result<i64, ValueError>;
30    fn try_float(self) -> Result<f64, ValueError>;
31    fn try_bytes(self) -> Result<Bytes, ValueError>;
32    fn try_boolean(self) -> Result<bool, ValueError>;
33    fn try_regex(self) -> Result<ValueRegex, ValueError>;
34    fn try_null(self) -> Result<(), ValueError>;
35    fn try_array(self) -> Result<Vec<Value>, ValueError>;
36    fn try_object(self) -> Result<ObjectMap, ValueError>;
37    fn try_timestamp(self) -> Result<DateTime<Utc>, ValueError>;
38
39    fn try_into_i64(&self) -> Result<i64, ValueError>;
40    fn try_into_f64(&self) -> Result<f64, ValueError>;
41
42    fn try_bytes_utf8_lossy(&self) -> Result<Cow<'_, str>, ValueError>;
43}
44
45impl VrlValueConvert for Value {
46    /// Convert a given [`Value`] into a [`Expression`] trait object.
47    fn into_expression(self) -> Box<dyn Expression> {
48        Box::new(Expr::from(self))
49    }
50
51    fn try_integer(self) -> Result<i64, ValueError> {
52        match self {
53            Value::Integer(v) => Ok(v),
54            _ => Err(ValueError::Expected {
55                got: self.kind(),
56                expected: Kind::integer(),
57            }),
58        }
59    }
60
61    fn try_into_i64(self: &Value) -> Result<i64, ValueError> {
62        match self {
63            Value::Integer(v) => Ok(*v),
64            #[allow(clippy::cast_possible_truncation)]
65            Value::Float(v) => Ok(v.into_inner() as i64),
66            _ => Err(ValueError::Coerce(self.kind(), Kind::integer())),
67        }
68    }
69
70    fn try_float(self) -> Result<f64, ValueError> {
71        match self {
72            Value::Float(v) => Ok(v.into_inner()),
73            _ => Err(ValueError::Expected {
74                got: self.kind(),
75                expected: Kind::float(),
76            }),
77        }
78    }
79
80    fn try_into_f64(&self) -> Result<f64, ValueError> {
81        match self {
82            #[allow(clippy::cast_precision_loss)]
83            Value::Integer(v) => Ok(*v as f64),
84            Value::Float(v) => Ok(v.into_inner()),
85            _ => Err(ValueError::Coerce(self.kind(), Kind::float())),
86        }
87    }
88
89    fn try_bytes(self) -> Result<Bytes, ValueError> {
90        match self {
91            Value::Bytes(v) => Ok(v),
92            _ => Err(ValueError::Expected {
93                got: self.kind(),
94                expected: Kind::bytes(),
95            }),
96        }
97    }
98
99    fn try_bytes_utf8_lossy(&self) -> Result<Cow<'_, str>, ValueError> {
100        self.as_str().ok_or(ValueError::Expected {
101            got: self.kind(),
102            expected: Kind::bytes(),
103        })
104    }
105
106    fn try_boolean(self) -> Result<bool, ValueError> {
107        match self {
108            Value::Boolean(v) => Ok(v),
109            _ => Err(ValueError::Expected {
110                got: self.kind(),
111                expected: Kind::boolean(),
112            }),
113        }
114    }
115
116    fn try_regex(self) -> Result<ValueRegex, ValueError> {
117        match self {
118            Value::Regex(v) => Ok(v),
119            _ => Err(ValueError::Expected {
120                got: self.kind(),
121                expected: Kind::regex(),
122            }),
123        }
124    }
125
126    fn try_null(self) -> Result<(), ValueError> {
127        match self {
128            Value::Null => Ok(()),
129            _ => Err(ValueError::Expected {
130                got: self.kind(),
131                expected: Kind::null(),
132            }),
133        }
134    }
135
136    fn try_array(self) -> Result<Vec<Value>, ValueError> {
137        match self {
138            Value::Array(v) => Ok(v),
139            _ => Err(ValueError::Expected {
140                got: self.kind(),
141                expected: Kind::array(Collection::any()),
142            }),
143        }
144    }
145
146    fn try_object(self) -> Result<ObjectMap, ValueError> {
147        match self {
148            Value::Object(v) => Ok(v),
149            _ => Err(ValueError::Expected {
150                got: self.kind(),
151                expected: Kind::object(Collection::any()),
152            }),
153        }
154    }
155
156    fn try_timestamp(self) -> Result<DateTime<Utc>, ValueError> {
157        match self {
158            Value::Timestamp(v) => Ok(v),
159            _ => Err(ValueError::Expected {
160                got: self.kind(),
161                expected: Kind::timestamp(),
162            }),
163        }
164    }
165}
166
167/// Converts from an `Expr` into a `Value`. This is only possible if the expression represents
168/// static values - `Literal`s and `Container`s containing `Literal`s.
169/// The error returns the expression back so it can be used in the error report.
170impl TryFrom<Expr> for Value {
171    type Error = Expr;
172
173    fn try_from(expr: Expr) -> Result<Self, Self::Error> {
174        match expr {
175            Expr::Literal(literal) => Ok(literal.to_value()),
176            Expr::Container(Container {
177                variant: Variant::Object(object),
178            }) => Ok(Value::Object(
179                object
180                    .iter()
181                    .map(|(key, value)| Ok((key.clone(), value.clone().try_into()?)))
182                    .collect::<Result<_, Self::Error>>()?,
183            )),
184            Expr::Container(Container {
185                variant: Variant::Array(array),
186            }) => Ok(Value::Array(
187                array
188                    .iter()
189                    .map(|value| value.clone().try_into())
190                    .collect::<Result<_, _>>()?,
191            )),
192            expr => Err(expr),
193        }
194    }
195}