rulemorph 0.3.4

YAML-based declarative data transformation engine for CSV/JSON to JSON
Documentation
use super::*;

pub(super) fn eval_map<'a>(
    op_step: &V2OpStep,
    pipe_value: EvalValue,
    record: &'a JsonValue,
    context: Option<&'a JsonValue>,
    out: &'a JsonValue,
    path: &str,
    ctx: &V2EvalContext<'a>,
) -> Result<EvalValue, TransformError> {
    if op_step.args.len() != 1 {
        return Err(TransformError::new(
            TransformErrorKind::ExprError,
            "map requires exactly one argument",
        )
        .with_path(path));
    }
    let array = match pipe_value {
        EvalValue::Missing => {
            return Ok(EvalValue::Missing);
        }
        EvalValue::Value(JsonValue::Array(items)) => items,
        EvalValue::Value(other) => {
            return Err(TransformError::new(
                TransformErrorKind::ExprError,
                format!("expr arg must be an array, got {:?}", other),
            )
            .with_path(path));
        }
    };
    let arg_path = format!("{}.args[0]", path);
    let limits = ctx.limits();
    let mut generated_items = 0usize;
    let mut results = Vec::new();
    for (index, item) in array.iter().enumerate() {
        let item_ctx = ctx
            .clone()
            .with_pipe_value(EvalValue::Value(item.clone()))
            .with_item(EvalItem { value: item, index });
        let value = eval_v2_expr(&op_step.args[0], record, context, out, &arg_path, &item_ctx)?;
        if let EvalValue::Value(value) = value {
            push_generated_array_item(&mut results, value, limits, path, &mut generated_items)?;
        }
    }
    Ok(EvalValue::Value(JsonValue::Array(results)))
}

pub(super) fn eval_flat_map<'a>(
    op_step: &V2OpStep,
    pipe_value: EvalValue,
    record: &'a JsonValue,
    context: Option<&'a JsonValue>,
    out: &'a JsonValue,
    path: &str,
    ctx: &V2EvalContext<'a>,
) -> Result<EvalValue, TransformError> {
    if op_step.args.len() != 1 {
        return Err(TransformError::new(
            TransformErrorKind::ExprError,
            "flat_map requires exactly one argument",
        )
        .with_path(path));
    }
    let array = eval_v2_array_from_eval_value(pipe_value.clone(), path)?;
    let arg_path = format!("{}.args[0]", path);
    let limits = ctx.limits();
    let mut generated_items = 0usize;
    let mut results = Vec::new();
    for (index, item) in array.iter().enumerate() {
        let item_ctx = ctx
            .clone()
            .with_pipe_value(EvalValue::Value(item.clone()))
            .with_item(EvalItem { value: item, index });
        let value =
            eval_v2_expr_or_null(&op_step.args[0], record, context, out, &arg_path, &item_ctx)?;
        match value {
            JsonValue::Array(items) => extend_generated_array_items(
                &mut results,
                items,
                limits,
                path,
                &mut generated_items,
            )?,
            value => {
                push_generated_array_item(&mut results, value, limits, path, &mut generated_items)?
            }
        }
    }
    Ok(EvalValue::Value(JsonValue::Array(results)))
}

pub(super) fn eval_zip_with<'a>(
    op_step: &V2OpStep,
    pipe_value: EvalValue,
    record: &'a JsonValue,
    context: Option<&'a JsonValue>,
    out: &'a JsonValue,
    path: &str,
    ctx: &V2EvalContext<'a>,
) -> Result<EvalValue, TransformError> {
    if op_step.args.len() < 2 {
        return Err(TransformError::new(
            TransformErrorKind::ExprError,
            "zip_with requires at least two arguments",
        )
        .with_path(path));
    }
    let mut arrays = Vec::new();
    arrays.push(eval_v2_array_from_eval_value(pipe_value.clone(), path)?);
    for (index, arg) in op_step.args.iter().enumerate().take(op_step.args.len() - 1) {
        let arg_path = format!("{}.args[{}]", path, index);
        let value = eval_v2_expr(arg, record, context, out, &arg_path, ctx)?;
        arrays.push(eval_v2_array_from_eval_value(value, &arg_path)?);
    }

    let min_len = arrays.iter().map(|items| items.len()).min().unwrap_or(0);
    let expr_index = op_step.args.len() - 1;
    let expr_path = format!("{}.args[{}]", path, expr_index);
    let expr = &op_step.args[expr_index];
    let limits = ctx.limits();
    let mut generated_items = 0usize;
    let mut results = Vec::with_capacity(min_len);
    for row_index in 0..min_len {
        let mut row = Vec::with_capacity(arrays.len());
        for array in &arrays {
            row.push(array[row_index].clone());
        }
        let row_value = JsonValue::Array(row);
        let item_ctx = ctx
            .clone()
            .with_pipe_value(EvalValue::Value(row_value.clone()))
            .with_item(EvalItem {
                value: &row_value,
                index: row_index,
            });
        let value = eval_v2_expr_or_null(expr, record, context, out, &expr_path, &item_ctx)?;
        push_generated_array_item(&mut results, value, limits, path, &mut generated_items)?;
    }
    Ok(EvalValue::Value(JsonValue::Array(results)))
}