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}