rulemorph 0.3.1

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

use super::{
    EvalItem, EvalValue, V2EvalContext, eval_v2_expr, eval_v2_expr_or_null, value_to_string,
};
use crate::error::{TransformError, TransformErrorKind};
use crate::v2_model::{V2Expr, V2OpStep};

mod keyed;
mod predicate;
mod reduce_fold;
mod sequence;
mod sort;

use keyed::eval_keyed_collection;
use predicate::{eval_filter, eval_find, eval_find_index, eval_partition};
use reduce_fold::{eval_fold, eval_reduce};
use sequence::{eval_flat_map, eval_map, eval_zip_with};
use sort::eval_sort_by;

pub(super) fn eval_collection_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> {
    match op_step.op.as_str() {
        "map" => eval_map(op_step, pipe_value, record, context, out, path, ctx),
        "filter" => eval_filter(op_step, pipe_value, record, context, out, path, ctx),
        "flat_map" => eval_flat_map(op_step, pipe_value, record, context, out, path, ctx),
        "group_by" | "key_by" | "distinct_by" => {
            eval_keyed_collection(op_step, pipe_value, record, context, out, path, ctx)
        }
        "partition" => eval_partition(op_step, pipe_value, record, context, out, path, ctx),
        "sort_by" => eval_sort_by(op_step, pipe_value, record, context, out, path, ctx),
        "find" => eval_find(op_step, pipe_value, record, context, out, path, ctx),
        "find_index" => eval_find_index(op_step, pipe_value, record, context, out, path, ctx),
        "reduce" => eval_reduce(op_step, pipe_value, record, context, out, path, ctx),
        "fold" => eval_fold(op_step, pipe_value, record, context, out, path, ctx),
        "zip_with" => eval_zip_with(op_step, pipe_value, record, context, out, path, ctx),
        _ => unreachable!("collection dispatcher only calls collection operators"),
    }
}

fn value_as_bool(value: &JsonValue, path: &str) -> Result<bool, TransformError> {
    match value {
        JsonValue::Bool(flag) => Ok(*flag),
        _ => Err(
            TransformError::new(TransformErrorKind::ExprError, "value must be a boolean")
                .with_path(path),
        ),
    }
}

fn eval_v2_predicate_expr<'a>(
    expr: &V2Expr,
    record: &'a JsonValue,
    context: Option<&'a JsonValue>,
    out: &'a JsonValue,
    path: &str,
    ctx: &V2EvalContext<'a>,
) -> Result<bool, TransformError> {
    match eval_v2_expr(expr, record, context, out, path, ctx)? {
        EvalValue::Missing => Ok(false),
        EvalValue::Value(value) => {
            if value.is_null() {
                return Ok(false);
            }
            value_as_bool(&value, path)
        }
    }
}

pub(super) fn eval_v2_key_expr_string<'a>(
    expr: &V2Expr,
    record: &'a JsonValue,
    context: Option<&'a JsonValue>,
    out: &'a JsonValue,
    path: &str,
    ctx: &V2EvalContext<'a>,
) -> Result<String, TransformError> {
    let value = match eval_v2_expr(expr, record, context, out, path, ctx)? {
        EvalValue::Missing => {
            return Err(TransformError::new(
                TransformErrorKind::ExprError,
                "expr arg must not be missing",
            )
            .with_path(path));
        }
        EvalValue::Value(value) => value,
    };
    if value.is_null() {
        return Err(TransformError::new(
            TransformErrorKind::ExprError,
            "expr arg must not be null",
        )
        .with_path(path));
    }
    value_to_string(&value, path)
}

pub(super) fn eval_v2_array_from_eval_value(
    value: EvalValue,
    path: &str,
) -> Result<Vec<JsonValue>, TransformError> {
    match value {
        EvalValue::Missing => Ok(Vec::new()),
        EvalValue::Value(value) => {
            if value.is_null() {
                Ok(Vec::new())
            } else if let JsonValue::Array(items) = value {
                Ok(items)
            } else {
                Err(
                    TransformError::new(TransformErrorKind::ExprError, "expr arg must be an array")
                        .with_path(path),
                )
            }
        }
    }
}