vrl/compiler/
expression.rs

1use std::fmt;
2
3use dyn_clone::{DynClone, clone_trait_object};
4
5pub use abort::Abort;
6pub use array::Array;
7pub use assignment::Assignment;
8pub use block::Block;
9pub use container::{Container, Variant};
10#[allow(clippy::module_name_repetitions)]
11pub use function::FunctionExpression;
12pub use function_argument::FunctionArgument;
13pub use function_call::FunctionCall;
14pub use group::Group;
15pub use if_statement::IfStatement;
16pub use literal::Literal;
17pub use noop::Noop;
18pub use not::Not;
19pub use object::Object;
20pub use op::Op;
21pub use predicate::Predicate;
22pub use query::{Query, Target};
23pub use r#return::Return;
24pub use unary::Unary;
25pub use variable::Variable;
26
27use crate::value::Value;
28
29#[allow(clippy::module_name_repetitions)]
30pub use super::ExpressionError;
31pub use super::Resolved;
32use super::state::{TypeInfo, TypeState};
33use super::{Context, TypeDef};
34
35mod abort;
36mod array;
37mod block;
38mod function_argument;
39mod group;
40mod if_statement;
41mod levenstein;
42mod noop;
43mod not;
44mod object;
45mod op;
46mod r#return;
47pub(crate) mod unary;
48mod variable;
49
50pub(crate) mod assignment;
51pub(crate) mod container;
52pub(crate) mod function;
53pub(crate) mod function_call;
54pub(crate) mod literal;
55pub(crate) mod predicate;
56pub mod query;
57
58#[allow(clippy::missing_errors_doc)]
59pub trait Expression: Send + Sync + fmt::Debug + DynClone {
60    /// Resolve an expression to a concrete [`Value`].
61    ///
62    /// This method is executed at runtime.
63    ///
64    /// An expression is allowed to fail, which aborts the running program.
65    fn resolve(&self, ctx: &mut Context) -> Resolved;
66
67    /// Resolve an expression to a value without any context, if possible.
68    /// This attempts to resolve expressions using only compile-time information.
69    ///
70    /// This returns `Some` for static expressions, or `None` for dynamic expressions.
71    fn resolve_constant(&self, _state: &TypeState) -> Option<Value> {
72        None
73    }
74
75    /// Resolve an expression to its [`TypeDef`] type definition.
76    /// This must be called with the _initial_ `TypeState`.
77    ///
78    /// Consider calling `type_info` instead if you want to capture changes in the type
79    /// state from side-effects.
80    fn type_def(&self, state: &TypeState) -> TypeDef {
81        self.type_info(state).result
82    }
83
84    /// Calculates the type state after an expression resolves, including the expression result itself.
85    /// This must be called with the _initial_ `TypeState`.
86    ///
87    /// Consider using `apply_type_info` instead if you want to just access
88    /// the expr result type, while updating an existing state.
89    fn type_info(&self, state: &TypeState) -> TypeInfo;
90
91    /// Applies state changes from the expression to the given state, and
92    /// returns the result type.
93    fn apply_type_info(&self, state: &mut TypeState) -> TypeDef {
94        let new_info = self.type_info(state);
95        *state = new_info.state;
96        new_info.result
97    }
98
99    /// Format the expression into a consistent style.
100    ///
101    /// This defaults to not formatting, so that function implementations don't
102    /// need to care about formatting (this is handled by the internal function
103    /// call expression).
104    fn format(&self) -> Option<String> {
105        None
106    }
107}
108
109clone_trait_object!(Expression);
110
111#[derive(Debug, Clone, PartialEq)]
112pub enum Expr {
113    Literal(Literal),
114    Container(Container),
115    IfStatement(IfStatement),
116    Op(Op),
117    Assignment(Assignment),
118    Query(Query),
119    FunctionCall(FunctionCall),
120    Variable(Variable),
121    Noop(Noop),
122    Unary(Unary),
123    Abort(Abort),
124    Return(Return),
125}
126
127impl Expr {
128    pub fn as_str(&self) -> &str {
129        use Expr::{
130            Abort, Assignment, Container, FunctionCall, IfStatement, Literal, Noop, Op, Query,
131            Return, Unary, Variable,
132        };
133        use container::Variant::{Array, Block, Group, Object};
134
135        match self {
136            Literal(..) => "literal",
137            Container(v) => match &v.variant {
138                Group(..) => "group",
139                Block(..) => "block",
140                Array(..) => "array",
141                Object(..) => "object",
142            },
143            IfStatement(..) => "if-statement",
144            Op(..) => "operation",
145            Assignment(..) => "assignment",
146            Query(..) => "query",
147            FunctionCall(..) => "function call",
148            Variable(..) => "variable call",
149            Noop(..) => "noop",
150            Unary(..) => "unary operation",
151            Abort(..) => "abort operation",
152            Return(..) => "return",
153        }
154    }
155
156    /// Attempts to resolve the current expression into a literal value.
157    ///
158    /// This function checks if the expression can be resolved to a constant
159    /// value within the given [`TypeState`]. If it can, the resolved value
160    /// is returned. Otherwise, an error is returned indicating that a
161    /// literal was expected but not found.
162    ///
163    /// # Arguments
164    ///
165    /// * `keyword` - A static string representing the keyword associated with this expression.
166    /// * `state` - A reference to the [`TypeState`] used to resolve constants.
167    ///
168    /// # Returns
169    ///
170    /// Returns a [`Result`] containing the resolved [`Value`] if the expression
171    /// evaluates to a constant. Otherwise, returns an [`Error`](`super::function::Error`) indicating
172    /// an unexpected expression.
173    ///
174    /// # Errors
175    ///
176    /// Returns [`super::function::Error::UnexpectedExpression`] if the
177    /// expression does not resolve to a literal.
178    pub fn as_literal(
179        &self,
180        keyword: &'static str,
181        state: &TypeState,
182    ) -> Result<Value, super::function::Error> {
183        match self.resolve_constant(state) {
184            Some(value) => Ok(value),
185            None => Err(super::function::Error::UnexpectedExpression {
186                keyword,
187                expected: "literal",
188                expr: self.clone(),
189            }),
190        }
191    }
192
193    /// Attempts to convert the value into an enumeration variant.
194    ///
195    /// This function checks whether the given value matches one of the provided
196    /// enumeration variants. If the value is not a valid variant, it returns an error.
197    ///
198    /// # Arguments
199    ///
200    /// * `keyword` - A static string representing the keyword associated with the enumeration.
201    /// * `variants` - A vector of possible valid `Value` variants.
202    /// * `state` - A reference to the `TypeState` used for type resolution.
203    ///
204    /// # Returns
205    ///
206    /// Returns `Ok(Value)` if the value is one of the allowed enumeration variants.
207    /// Otherwise, it returns an `Err(super::function::Error::InvalidEnumVariant)`.
208    ///
209    /// # Errors
210    ///
211    /// This function returns an `InvalidEnumVariant` error if the value is not found
212    /// in the provided list of valid variants.
213    pub fn as_enum(
214        &self,
215        keyword: &'static str,
216        variants: Vec<Value>,
217        state: &TypeState,
218    ) -> Result<Value, super::function::Error> {
219        let value = self.as_literal(keyword, state)?;
220        variants.iter().find(|v| **v == value).cloned().ok_or(
221            super::function::Error::InvalidEnumVariant {
222                keyword,
223                value,
224                variants,
225            },
226        )
227    }
228}
229
230impl Expression for Expr {
231    fn resolve(&self, ctx: &mut Context) -> Resolved {
232        use Expr::{
233            Abort, Assignment, Container, FunctionCall, IfStatement, Literal, Noop, Op, Query,
234            Return, Unary, Variable,
235        };
236
237        match self {
238            Literal(v) => v.resolve(ctx),
239            Container(v) => v.resolve(ctx),
240            IfStatement(v) => v.resolve(ctx),
241            Op(v) => v.resolve(ctx),
242            Assignment(v) => v.resolve(ctx),
243            Query(v) => v.resolve(ctx),
244            FunctionCall(v) => v.resolve(ctx),
245            Variable(v) => v.resolve(ctx),
246            Noop(v) => v.resolve(ctx),
247            Unary(v) => v.resolve(ctx),
248            Abort(v) => v.resolve(ctx),
249            Return(v) => v.resolve(ctx),
250        }
251    }
252
253    fn resolve_constant(&self, state: &TypeState) -> Option<Value> {
254        use Expr::{
255            Abort, Assignment, Container, FunctionCall, IfStatement, Literal, Noop, Op, Query,
256            Return, Unary, Variable,
257        };
258
259        match self {
260            Literal(v) => Expression::resolve_constant(v, state),
261            Container(v) => Expression::resolve_constant(v, state),
262            IfStatement(v) => Expression::resolve_constant(v, state),
263            Op(v) => Expression::resolve_constant(v, state),
264            Assignment(v) => Expression::resolve_constant(v, state),
265            Query(v) => Expression::resolve_constant(v, state),
266            FunctionCall(v) => Expression::resolve_constant(v, state),
267            Variable(v) => Expression::resolve_constant(v, state),
268            Noop(v) => Expression::resolve_constant(v, state),
269            Unary(v) => Expression::resolve_constant(v, state),
270            Abort(v) => Expression::resolve_constant(v, state),
271            Return(v) => Expression::resolve_constant(v, state),
272        }
273    }
274
275    fn type_info(&self, state: &TypeState) -> TypeInfo {
276        use Expr::{
277            Abort, Assignment, Container, FunctionCall, IfStatement, Literal, Noop, Op, Query,
278            Return, Unary, Variable,
279        };
280
281        match self {
282            Literal(v) => v.type_info(state),
283            Container(v) => v.type_info(state),
284            IfStatement(v) => v.type_info(state),
285            Op(v) => v.type_info(state),
286            Assignment(v) => v.type_info(state),
287            Query(v) => v.type_info(state),
288            FunctionCall(v) => v.type_info(state),
289            Variable(v) => v.type_info(state),
290            Noop(v) => v.type_info(state),
291            Unary(v) => v.type_info(state),
292            Abort(v) => v.type_info(state),
293            Return(v) => v.type_info(state),
294        }
295    }
296}
297
298impl fmt::Display for Expr {
299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300        use Expr::{
301            Abort, Assignment, Container, FunctionCall, IfStatement, Literal, Noop, Op, Query,
302            Return, Unary, Variable,
303        };
304
305        match self {
306            Literal(v) => v.fmt(f),
307            Container(v) => v.fmt(f),
308            IfStatement(v) => v.fmt(f),
309            Op(v) => v.fmt(f),
310            Assignment(v) => v.fmt(f),
311            Query(v) => v.fmt(f),
312            FunctionCall(v) => v.fmt(f),
313            Variable(v) => v.fmt(f),
314            Noop(v) => v.fmt(f),
315            Unary(v) => v.fmt(f),
316            Abort(v) => v.fmt(f),
317            Return(v) => v.fmt(f),
318        }
319    }
320}
321
322// -----------------------------------------------------------------------------
323
324impl From<Literal> for Expr {
325    fn from(literal: Literal) -> Self {
326        Expr::Literal(literal)
327    }
328}
329
330impl From<Container> for Expr {
331    fn from(container: Container) -> Self {
332        Expr::Container(container)
333    }
334}
335
336impl From<IfStatement> for Expr {
337    fn from(if_statement: IfStatement) -> Self {
338        Expr::IfStatement(if_statement)
339    }
340}
341
342impl From<Op> for Expr {
343    fn from(op: Op) -> Self {
344        Expr::Op(op)
345    }
346}
347
348impl From<Assignment> for Expr {
349    fn from(assignment: Assignment) -> Self {
350        Expr::Assignment(assignment)
351    }
352}
353
354impl From<Query> for Expr {
355    fn from(query: Query) -> Self {
356        Expr::Query(query)
357    }
358}
359
360impl From<FunctionCall> for Expr {
361    fn from(function_call: FunctionCall) -> Self {
362        Expr::FunctionCall(function_call)
363    }
364}
365
366impl From<Variable> for Expr {
367    fn from(variable: Variable) -> Self {
368        Expr::Variable(variable)
369    }
370}
371
372impl From<Noop> for Expr {
373    fn from(noop: Noop) -> Self {
374        Expr::Noop(noop)
375    }
376}
377
378impl From<Unary> for Expr {
379    fn from(unary: Unary) -> Self {
380        Expr::Unary(unary)
381    }
382}
383
384impl From<Abort> for Expr {
385    fn from(abort: Abort) -> Self {
386        Expr::Abort(abort)
387    }
388}
389
390impl From<Return> for Expr {
391    fn from(r#return: Return) -> Self {
392        Expr::Return(r#return)
393    }
394}
395
396impl From<Value> for Expr {
397    fn from(value: Value) -> Self {
398        use std::collections::BTreeMap;
399
400        use crate::value::Value::{
401            Array, Boolean, Bytes, Float, Integer, Null, Object, Regex, Timestamp,
402        };
403
404        match value {
405            Bytes(v) => Literal::from(v).into(),
406            Integer(v) => Literal::from(v).into(),
407            Float(v) => Literal::from(v).into(),
408            Boolean(v) => Literal::from(v).into(),
409            Object(v) => {
410                let object = super::expression::Object::from(
411                    v.into_iter()
412                        .map(|(k, v)| (k, v.into()))
413                        .collect::<BTreeMap<_, _>>(),
414                );
415
416                Container::new(container::Variant::from(object)).into()
417            }
418            Array(v) => {
419                let array = super::expression::Array::from(
420                    v.into_iter().map(Expr::from).collect::<Vec<_>>(),
421                );
422
423                Container::new(container::Variant::from(array)).into()
424            }
425            Timestamp(v) => Literal::from(v).into(),
426            Regex(v) => Literal::from(v).into(),
427            Null => Literal::from(()).into(),
428        }
429    }
430}
431
432// Helper trait
433#[allow(clippy::missing_errors_doc)]
434pub trait ExpressionExt {
435    fn map_resolve(&self, ctx: &mut Context) -> Result<Option<Value>, ExpressionError>;
436    fn map_resolve_with_default<F>(&self, ctx: &mut Context, default_fn: F) -> Resolved
437    where
438        F: FnOnce() -> Value;
439}
440
441impl ExpressionExt for Option<Box<dyn Expression>> {
442    fn map_resolve(&self, ctx: &mut Context) -> Result<Option<Value>, ExpressionError> {
443        self.as_ref().map(|expr| expr.resolve(ctx)).transpose()
444    }
445
446    fn map_resolve_with_default<F>(&self, ctx: &mut Context, default_fn: F) -> Resolved
447    where
448        F: FnOnce() -> Value,
449    {
450        Ok(self.map_resolve(ctx)?.unwrap_or_else(default_fn))
451    }
452}