use std::collections::BTreeMap;
use crate::compiler::{
Context, ExpressionError,
state::RuntimeState,
value::{Kind, VrlValueConvert},
};
use crate::parser::ast::Ident;
use crate::value::{
KeyString, Value,
kind::{Collection, Field, Index},
};
use super::Example;
#[derive(Debug)]
pub struct Definition {
pub inputs: Vec<Input>,
pub is_iterator: bool,
}
#[derive(Debug, Clone)]
pub struct Input {
pub parameter_keyword: &'static str,
pub kind: Kind,
pub variables: Vec<Variable>,
pub output: Output,
pub example: Example,
}
#[derive(Debug, Clone)]
pub struct Variable {
pub kind: VariableKind,
}
#[derive(Debug, Clone)]
pub enum VariableKind {
Exact(Kind),
Target,
TargetInnerValue,
TargetInnerKey,
}
#[derive(Debug, Clone)]
pub enum Output {
Array {
elements: Vec<Kind>,
},
Object {
fields: BTreeMap<&'static str, Kind>,
},
Kind(
Kind,
),
}
impl Output {
#[must_use]
pub fn into_kind(self) -> Kind {
match self {
Output::Array { elements } => {
let collection: Collection<Index> = elements
.into_iter()
.enumerate()
.map(|(i, k)| (i.into(), k))
.collect::<BTreeMap<_, _>>()
.into();
collection.into()
}
Output::Object { fields } => {
let collection: Collection<Field> = fields
.into_iter()
.map(|(k, v)| (k.into(), v))
.collect::<BTreeMap<_, _>>()
.into();
collection.into()
}
Output::Kind(kind) => kind,
}
}
}
pub struct Runner<'a, T> {
pub(crate) variables: &'a [Ident],
pub(crate) runner: T,
}
#[allow(clippy::missing_errors_doc)]
impl<'a, T> Runner<'a, T>
where
T: Fn(&mut Context) -> Result<Value, ExpressionError>,
{
pub fn new(variables: &'a [Ident], runner: T) -> Self {
Self { variables, runner }
}
pub fn run_key_value(
&self,
ctx: &mut Context,
key: &str,
value: &Value,
) -> Result<Value, ExpressionError> {
let cloned_key = key.to_owned();
let cloned_value = value.clone();
let key_ident = self.ident(0);
let value_ident = self.ident(1);
let old_key = insert(ctx.state_mut(), key_ident, cloned_key.into());
let old_value = insert(ctx.state_mut(), value_ident, cloned_value);
let result = match (self.runner)(ctx) {
Ok(value) | Err(ExpressionError::Return { value, .. }) => Ok(value),
err @ Err(_) => err,
};
let value = result?;
cleanup(ctx.state_mut(), key_ident, old_key);
cleanup(ctx.state_mut(), value_ident, old_value);
Ok(value)
}
pub fn run_index_value(
&self,
ctx: &mut Context,
index: usize,
value: &Value,
) -> Result<Value, ExpressionError> {
let cloned_value = value.clone();
let index_ident = self.ident(0);
let value_ident = self.ident(1);
let old_index = insert(ctx.state_mut(), index_ident, index.into());
let old_value = insert(ctx.state_mut(), value_ident, cloned_value);
let value = (self.runner)(ctx)?;
cleanup(ctx.state_mut(), index_ident, old_index);
cleanup(ctx.state_mut(), value_ident, old_value);
Ok(value)
}
pub fn map_key(&self, ctx: &mut Context, key: &mut KeyString) -> Result<(), ExpressionError> {
let cloned_key = key.clone();
let ident = self.ident(0);
let old_key = insert(ctx.state_mut(), ident, cloned_key.into());
*key = (self.runner)(ctx)?.try_bytes_utf8_lossy()?.into();
cleanup(ctx.state_mut(), ident, old_key);
Ok(())
}
pub fn map_value(&self, ctx: &mut Context, value: &mut Value) -> Result<(), ExpressionError> {
let cloned_value = value.clone();
let ident = self.ident(0);
let old_value = insert(ctx.state_mut(), ident, cloned_value);
*value = (self.runner)(ctx)?;
cleanup(ctx.state_mut(), ident, old_value);
Ok(())
}
fn ident(&self, index: usize) -> Option<&Ident> {
self.variables
.get(index)
.and_then(|v| (!v.is_empty()).then_some(v))
}
}
fn insert(state: &mut RuntimeState, ident: Option<&Ident>, data: Value) -> Option<Value> {
ident.and_then(|ident| state.swap_variable(ident.clone(), data))
}
fn cleanup(state: &mut RuntimeState, ident: Option<&Ident>, data: Option<Value>) {
match (ident, data) {
(Some(ident), Some(value)) => {
state.insert_variable(ident.clone(), value);
}
(Some(ident), None) => state.remove_variable(ident),
_ => {}
}
}