vrl/compiler/
runtime.rs

1use std::{error::Error, fmt};
2
3use crate::path::OwnedTargetPath;
4use crate::value::Value;
5
6use super::ExpressionError;
7use super::TimeZone;
8use super::{Context, Program, Target, state};
9
10#[allow(clippy::module_name_repetitions)]
11pub type RuntimeResult = Result<Value, Terminate>;
12
13#[derive(Debug, Default)]
14pub struct Runtime {
15    state: state::RuntimeState,
16}
17
18/// The error raised if the runtime is terminated.
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum Terminate {
21    /// A manual `abort` call.
22    ///
23    /// This is an intentional termination that does not result in an
24    /// `Ok(Value)` result, but should neither be interpreted as an unexpected
25    /// outcome.
26    Abort(ExpressionError),
27
28    /// An unexpected program termination.
29    Error(ExpressionError),
30}
31
32impl Terminate {
33    #[must_use]
34    pub fn get_expression_error(self) -> ExpressionError {
35        match self {
36            Terminate::Error(error) | Terminate::Abort(error) => error,
37        }
38    }
39}
40
41impl fmt::Display for Terminate {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        match self {
44            Terminate::Error(error) | Terminate::Abort(error) => error.fmt(f),
45        }
46    }
47}
48
49impl Error for Terminate {
50    fn source(&self) -> Option<&(dyn Error + 'static)> {
51        None
52    }
53}
54
55impl Runtime {
56    #[must_use]
57    pub fn new(state: state::RuntimeState) -> Self {
58        Self { state }
59    }
60
61    #[must_use]
62    pub fn is_empty(&self) -> bool {
63        self.state.is_empty()
64    }
65
66    pub fn clear(&mut self) {
67        self.state.clear();
68    }
69
70    /// Resolves the provided [`Program`] to completion using the given [`Target`].
71    ///
72    /// This function ensures that the target contains a valid root object before proceeding.
73    /// If the target is invalid or missing, an error is returned. The resolution process
74    /// is performed using a [`Context`] that maintains execution state and timezone information.
75    ///
76    /// # Arguments
77    ///
78    /// * `target` - A mutable reference to an object implementing the [`Target`] trait. This
79    ///   serves as the execution environment for resolving the program.
80    /// * `program` - A reference to the [`Program`] that needs to be resolved.
81    /// * `timezone` - A reference to the [`TimeZone`] used for resolving time-dependent expressions.
82    ///
83    /// # Returns
84    ///
85    /// Returns a [`RuntimeResult`], which is either:
86    /// - `Ok(value)`: The program resolved successfully, producing a value.
87    /// - `Err(Terminate::Error)`: A fatal error occurred during resolution.
88    /// - `Err(Terminate::Abort)`: The resolution was aborted due to a non-fatal expression error.
89    ///
90    /// # Errors
91    ///
92    /// The function may return an error in the following cases:
93    /// - If the target does not contain a valid root object, an error is returned.
94    /// - If the resolution process encounters an [`ExpressionError::Error`].
95    /// - If the program execution results in an [`ExpressionError::Abort`], [`ExpressionError::Fallible`], or [`ExpressionError::Missing`], the function aborts with `Terminate::Abort`.
96    pub fn resolve(
97        &mut self,
98        target: &mut dyn Target,
99        program: &Program,
100        timezone: &TimeZone,
101    ) -> RuntimeResult {
102        // Validate that the path is a value.
103        match target.target_get(&OwnedTargetPath::event_root()) {
104            Ok(Some(_)) => {}
105            Ok(None) => {
106                return Err(Terminate::Error(
107                    "expected target object, got nothing".to_owned().into(),
108                ));
109            }
110            Err(err) => {
111                return Err(Terminate::Error(
112                    format!("error querying target object: {err}").into(),
113                ));
114            }
115        }
116
117        let mut ctx = Context::new(target, &mut self.state, timezone);
118
119        match program.resolve(&mut ctx) {
120            Ok(value) | Err(ExpressionError::Return { value, .. }) => Ok(value),
121            Err(
122                err @ (ExpressionError::Abort { .. }
123                | ExpressionError::Fallible { .. }
124                | ExpressionError::Missing { .. }),
125            ) => Err(Terminate::Abort(err)),
126            Err(err @ ExpressionError::Error { .. }) => Err(Terminate::Error(err)),
127        }
128    }
129}