rulemorph 0.3.4

YAML-based declarative data transformation engine for CSV/JSON to JSON
Documentation
use serde_json::Value as JsonValue;

use super::super::{EvalValue, V2EvalContext, eval_v2_expr, eval_v2_op_with_v1_fallback};
use crate::error::{TransformError, TransformErrorKind};
use crate::v2_model::{V2Expr, V2OpStep, V2Pipe, V2Start};

pub(super) fn eval_projection_op<'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.is_empty() {
        return Err(TransformError::new(
            TransformErrorKind::ExprError,
            format!("{} requires at least one argument", op_step.op),
        )
        .with_path(format!("{}.args", path)));
    }

    let mut path_values = Vec::new();
    for (index, arg) in op_step.args.iter().enumerate() {
        let arg_path = format!("{}.args[{}]", path, index);
        let value = match eval_v2_expr(arg, record, context, out, &arg_path, ctx)? {
            EvalValue::Missing => return Ok(EvalValue::Missing),
            EvalValue::Value(value) => value,
        };
        if value.is_null() {
            return Err(TransformError::new(
                TransformErrorKind::ExprError,
                "expr arg must not be null",
            )
            .with_path(arg_path));
        }
        match value {
            JsonValue::String(path_value) => {
                path_values.push(JsonValue::String(path_value));
            }
            JsonValue::Array(items) => {
                for (item_index, item) in items.iter().enumerate() {
                    let item_path = format!("{}.args[{}][{}]", path, index, item_index);
                    let path_value = item.as_str().ok_or_else(|| {
                        TransformError::new(
                            TransformErrorKind::ExprError,
                            "paths must be a string or array of strings",
                        )
                        .with_path(item_path)
                    })?;
                    path_values.push(JsonValue::String(path_value.to_string()));
                }
            }
            _ => {
                return Err(TransformError::new(
                    TransformErrorKind::ExprError,
                    "paths must be a string or array of strings",
                )
                .with_path(arg_path));
            }
        }
    }

    let normalized_op = V2OpStep {
        op: op_step.op.clone(),
        args: vec![V2Expr::Pipe(V2Pipe {
            start: V2Start::Literal(JsonValue::Array(path_values)),
            steps: vec![],
        })],
    };
    eval_v2_op_with_v1_fallback(&normalized_op, pipe_value, record, context, out, path, ctx)
}