vrl/compiler/function/
closure.rs

1use std::collections::BTreeMap;
2
3use crate::compiler::{
4    Context, ExpressionError,
5    state::RuntimeState,
6    value::{Kind, VrlValueConvert},
7};
8use crate::parser::ast::Ident;
9use crate::value::{
10    KeyString, Value,
11    kind::{Collection, Field, Index},
12};
13
14use super::Example;
15
16/// The definition of a function-closure block a function expects to
17/// receive.
18#[derive(Debug)]
19pub struct Definition {
20    /// A list of input configurations valid for this closure definition.
21    pub inputs: Vec<Input>,
22
23    /// Defines whether the closure is expected to iterate over the elements of
24    /// a collection.
25    ///
26    /// If this is `true`, the compiler will (1) reject any non-iterable types
27    /// passed to this closure, and (2) use the type definition of inner
28    /// collection elements to determine the eventual type definition of the
29    /// closure variable(s) (see `Variable`).
30    pub is_iterator: bool,
31}
32
33/// One input variant for a function-closure.
34///
35/// A closure can support different variable input shapes, depending on the
36/// type of a given parameter of the function.
37///
38/// For example, the `for_each` function takes either an `Object` or an `Array`
39/// for the `value` parameter, and the closure it takes either accepts `|key,
40/// value|`, where "key" is always a string, or `|index, value|` where "index"
41/// is always a number, depending on the parameter input type.
42#[derive(Debug, Clone)]
43pub struct Input {
44    /// The parameter keyword upon which this closure input variant depends on.
45    pub parameter_keyword: &'static str,
46
47    /// The value kind this closure input expects from the parameter.
48    pub kind: Kind,
49
50    /// The list of variables attached to this closure input type.
51    pub variables: Vec<Variable>,
52
53    /// The return type this input variant expects the closure to have.
54    pub output: Output,
55
56    /// An example matching the given input.
57    pub example: Example,
58}
59
60/// One variable input for a closure.
61///
62/// For example, in `{ |foo, bar| ... }`, `foo` and `bar` are each
63/// a `Variable`.
64#[derive(Debug, Clone)]
65pub struct Variable {
66    /// The value kind this variable will return when called.
67    ///
68    /// If set to `None`, the compiler is expected to provide this value at
69    /// compile-time, or resort to `Kind::any()` if no information is known.
70    pub kind: VariableKind,
71}
72
73/// The [`Value`] kind expected to be returned by a [`Variable`].
74#[derive(Debug, Clone)]
75pub enum VariableKind {
76    /// An exact [`Kind`] means this variable is guaranteed to always contain
77    /// a value that resolves to this kind.
78    ///
79    /// For example, in `map_keys`, it is known that the first (and only)
80    /// variable the closure takes will be a `Kind::bytes()`.
81    Exact(Kind),
82
83    /// The variable [`Kind`] is inferred from the target of the closure.
84    Target,
85
86    /// The variable [`Kind`] is inferred from the inner kind of the target of
87    /// the closure. This requires the closure target to be a collection type.
88    TargetInnerValue,
89
90    /// The variable [`Kind`] is inferred from the key or index type of the
91    /// target. If the target is known to be exactly an object, this is always
92    /// a `Value::bytes()`, if it's known to be exactly an array, it is
93    /// a `Value::integer()`, otherwise it is one of the two.
94    TargetInnerKey,
95}
96
97/// The output type required by the closure block.
98#[derive(Debug, Clone)]
99pub enum Output {
100    Array {
101        /// The number, and kind of elements expected.
102        elements: Vec<Kind>,
103    },
104
105    Object {
106        /// The field names, and value kinds expected.
107        fields: BTreeMap<&'static str, Kind>,
108    },
109
110    Kind(
111        /// The expected kind.
112        Kind,
113    ),
114}
115
116impl Output {
117    #[must_use]
118    pub fn into_kind(self) -> Kind {
119        match self {
120            Output::Array { elements } => {
121                let collection: Collection<Index> = elements
122                    .into_iter()
123                    .enumerate()
124                    .map(|(i, k)| (i.into(), k))
125                    .collect::<BTreeMap<_, _>>()
126                    .into();
127
128                collection.into()
129            }
130            Output::Object { fields } => {
131                let collection: Collection<Field> = fields
132                    .into_iter()
133                    .map(|(k, v)| (k.into(), v))
134                    .collect::<BTreeMap<_, _>>()
135                    .into();
136
137                collection.into()
138            }
139            Output::Kind(kind) => kind,
140        }
141    }
142}
143
144pub struct Runner<'a, T> {
145    pub(crate) variables: &'a [Ident],
146    pub(crate) runner: T,
147}
148
149#[allow(clippy::missing_errors_doc)]
150impl<'a, T> Runner<'a, T>
151where
152    T: Fn(&mut Context) -> Result<Value, ExpressionError>,
153{
154    pub fn new(variables: &'a [Ident], runner: T) -> Self {
155        Self { variables, runner }
156    }
157
158    /// Run the closure to completion, given the provided key/value pair, and
159    /// the runtime context.
160    ///
161    /// The provided values are *NOT* mutated during the run. See `map_key` or
162    /// `map_value` for mutating alternatives.
163    pub fn run_key_value(
164        &self,
165        ctx: &mut Context,
166        key: &str,
167        value: &Value,
168    ) -> Result<Value, ExpressionError> {
169        // TODO: we need to allow `LocalEnv` to take a mutable reference to
170        // values, instead of owning them.
171        let cloned_key = key.to_owned();
172        let cloned_value = value.clone();
173
174        let key_ident = self.ident(0);
175        let value_ident = self.ident(1);
176
177        let old_key = insert(ctx.state_mut(), key_ident, cloned_key.into());
178        let old_value = insert(ctx.state_mut(), value_ident, cloned_value);
179
180        let result = match (self.runner)(ctx) {
181            Ok(value) | Err(ExpressionError::Return { value, .. }) => Ok(value),
182            err @ Err(_) => err,
183        };
184
185        let value = result?;
186
187        cleanup(ctx.state_mut(), key_ident, old_key);
188        cleanup(ctx.state_mut(), value_ident, old_value);
189
190        Ok(value)
191    }
192
193    /// Run the closure to completion, given the provided index/value pair, and
194    /// the runtime context.
195    ///
196    /// The provided values are *NOT* mutated during the run. See `map_key` or
197    /// `map_value` for mutating alternatives.
198    pub fn run_index_value(
199        &self,
200        ctx: &mut Context,
201        index: usize,
202        value: &Value,
203    ) -> Result<Value, ExpressionError> {
204        // TODO: we need to allow `LocalEnv` to take a mutable reference to
205        // values, instead of owning them.
206        let cloned_value = value.clone();
207
208        let index_ident = self.ident(0);
209        let value_ident = self.ident(1);
210
211        let old_index = insert(ctx.state_mut(), index_ident, index.into());
212        let old_value = insert(ctx.state_mut(), value_ident, cloned_value);
213
214        let value = (self.runner)(ctx)?;
215
216        cleanup(ctx.state_mut(), index_ident, old_index);
217        cleanup(ctx.state_mut(), value_ident, old_value);
218
219        Ok(value)
220    }
221
222    /// Run the closure to completion, given the provided key, and the runtime
223    /// context.
224    ///
225    /// The provided key is *MUTATED* by overwriting the key with the return
226    /// value of the closure after completion.
227    ///
228    /// See `run_key_value` and `run_index_value` for immutable alternatives.
229    pub fn map_key(&self, ctx: &mut Context, key: &mut KeyString) -> Result<(), ExpressionError> {
230        // TODO: we need to allow `LocalEnv` to take a mutable reference to
231        // values, instead of owning them.
232        let cloned_key = key.clone();
233        let ident = self.ident(0);
234        let old_key = insert(ctx.state_mut(), ident, cloned_key.into());
235
236        *key = (self.runner)(ctx)?.try_bytes_utf8_lossy()?.into();
237
238        cleanup(ctx.state_mut(), ident, old_key);
239
240        Ok(())
241    }
242
243    /// Run the closure to completion, given the provided value, and the runtime
244    /// context.
245    ///
246    /// The provided value is *MUTATED* by overwriting the value with the return
247    /// value of the closure after completion.
248    ///
249    /// See `run_key_value` and `run_index_value` for immutable alternatives.
250    pub fn map_value(&self, ctx: &mut Context, value: &mut Value) -> Result<(), ExpressionError> {
251        // TODO: we need to allow `LocalEnv` to take a mutable reference to
252        // values, instead of owning them.
253        let cloned_value = value.clone();
254        let ident = self.ident(0);
255        let old_value = insert(ctx.state_mut(), ident, cloned_value);
256
257        *value = (self.runner)(ctx)?;
258
259        cleanup(ctx.state_mut(), ident, old_value);
260
261        Ok(())
262    }
263
264    fn ident(&self, index: usize) -> Option<&Ident> {
265        self.variables
266            .get(index)
267            .and_then(|v| (!v.is_empty()).then_some(v))
268    }
269}
270
271fn insert(state: &mut RuntimeState, ident: Option<&Ident>, data: Value) -> Option<Value> {
272    ident.and_then(|ident| state.swap_variable(ident.clone(), data))
273}
274
275fn cleanup(state: &mut RuntimeState, ident: Option<&Ident>, data: Option<Value>) {
276    match (ident, data) {
277        (Some(ident), Some(value)) => {
278            state.insert_variable(ident.clone(), value);
279        }
280        (Some(ident), None) => state.remove_variable(ident),
281        _ => {}
282    }
283}