rulemorph 0.3.3

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

#[allow(clippy::too_many_arguments)]
pub(super) fn eval_v2_reduce_fold_traced<'a>(
    op_step: &crate::v2_model::V2OpStep,
    pipe_value: V2EvalValue,
    record: &'a JsonValue,
    context: Option<&'a JsonValue>,
    out: &'a JsonValue,
    path: &str,
    ctx: &V2EvalContext<'a>,
    collector: &mut TraceCollector,
) -> Result<V2EvalValue, TransformError> {
    match op_step.op.as_str() {
        "reduce" => eval_v2_reduce_traced(
            op_step, pipe_value, record, context, out, path, ctx, collector,
        ),
        "fold" => eval_v2_fold_traced(
            op_step, pipe_value, record, context, out, path, ctx, collector,
        ),
        _ => unreachable!("non reduce/fold operator dispatched to reduce_fold"),
    }
}

#[allow(clippy::too_many_arguments)]
fn eval_v2_reduce_traced<'a>(
    op_step: &crate::v2_model::V2OpStep,
    pipe_value: V2EvalValue,
    record: &'a JsonValue,
    context: Option<&'a JsonValue>,
    out: &'a JsonValue,
    path: &str,
    ctx: &V2EvalContext<'a>,
    collector: &mut TraceCollector,
) -> Result<V2EvalValue, TransformError> {
    let operator = "reduce";
    if op_step.args.len() != 1 {
        return Err(TransformError::new(
            TransformErrorKind::ExprError,
            "reduce requires exactly one argument",
        )
        .with_path(path));
    }
    let array = v2_eval_array_from_value(pipe_value, path)?;
    if array.is_empty() {
        return Ok(V2EvalValue::Value(JsonValue::Null));
    }
    let expr_path = format!("{}.args[0]", path);
    let mut acc = array[0].clone();
    for (index, item) in array.iter().enumerate().skip(1) {
        let item_path = format!("{}[{}]", path, index);
        emit_v2_collection_item_start(collector, &item_path, operator, index, item);
        let item_ctx = ctx
            .clone()
            .with_pipe_value(V2EvalValue::Value(item.clone()))
            .with_item(V2EvalItem { value: item, index })
            .with_acc(&acc);
        let value = eval_v2_expr_or_null_traced(
            &op_step.args[0],
            record,
            context,
            out,
            &expr_path,
            &item_ctx,
            collector,
        )?;
        let output = V2EvalValue::Value(value.clone());
        emit_v2_arg_eval(collector, &expr_path, 0, operator, &output);
        acc = value;
        finish_v2_collection_item(collector, &item_path, operator, index, &output, None);
    }
    Ok(V2EvalValue::Value(acc))
}

#[allow(clippy::too_many_arguments)]
fn eval_v2_fold_traced<'a>(
    op_step: &crate::v2_model::V2OpStep,
    pipe_value: V2EvalValue,
    record: &'a JsonValue,
    context: Option<&'a JsonValue>,
    out: &'a JsonValue,
    path: &str,
    ctx: &V2EvalContext<'a>,
    collector: &mut TraceCollector,
) -> Result<V2EvalValue, TransformError> {
    let operator = "fold";
    if op_step.args.len() != 2 {
        return Err(TransformError::new(
            TransformErrorKind::ExprError,
            "fold requires exactly two arguments",
        )
        .with_path(path));
    }
    let array = v2_eval_array_from_value(pipe_value, path)?;
    let init_path = format!("{}.args[0]", path);
    let initial = eval_v2_expr_traced(
        &op_step.args[0],
        record,
        context,
        out,
        &init_path,
        ctx,
        collector,
    )?;
    emit_v2_arg_eval(collector, &init_path, 0, operator, &initial);
    let mut acc = match initial {
        V2EvalValue::Missing => return Ok(V2EvalValue::Missing),
        V2EvalValue::Value(value) => value,
    };
    let expr_path = format!("{}.args[1]", path);
    for (index, item) in array.iter().enumerate() {
        let item_path = format!("{}[{}]", path, index);
        emit_v2_collection_item_start(collector, &item_path, operator, index, item);
        let item_ctx = ctx
            .clone()
            .with_pipe_value(V2EvalValue::Value(item.clone()))
            .with_item(V2EvalItem { value: item, index })
            .with_acc(&acc);
        let value = eval_v2_expr_or_null_traced(
            &op_step.args[1],
            record,
            context,
            out,
            &expr_path,
            &item_ctx,
            collector,
        )?;
        let output = V2EvalValue::Value(value.clone());
        emit_v2_arg_eval(collector, &expr_path, 1, operator, &output);
        acc = value;
        finish_v2_collection_item(collector, &item_path, operator, index, &output, None);
    }
    Ok(V2EvalValue::Value(acc))
}