rulemorph 0.3.4

YAML-based declarative data transformation engine for CSV/JSON to JSON
Documentation
use std::collections::HashSet;

use crate::error::ErrorCode;
use crate::model::{Expr, ExprChain, ExprOp};
use crate::path::PathToken;

mod op_args;

use self::op_args::validate_chain_op_args;
use super::super::ValidationCtx;
use super::super::op_inventory::{element_expr_scope, is_valid_op};
use super::super::scope::LocalScope;
use super::validate_expr;

pub(super) fn validate_chain(
    expr_chain: &ExprChain,
    base_path: &str,
    produced_targets: &HashSet<Vec<PathToken>>,
    ctx: &mut ValidationCtx<'_>,
    scope: LocalScope,
) {
    if expr_chain.chain.is_empty() {
        ctx.push(
            ErrorCode::InvalidExprShape,
            "expr.chain must be a non-empty array",
            format!("{}.chain", base_path),
        );
        return;
    }

    for (index, item) in expr_chain.chain.iter().enumerate() {
        let item_path = format!("{}.chain[{}]", base_path, index);
        if index == 0 {
            validate_expr(item, &item_path, produced_targets, ctx, scope);
            continue;
        }

        match item {
            Expr::Op(expr_op) => {
                validate_chain_op(expr_op, &item_path, produced_targets, ctx, scope);
            }
            _ => {
                ctx.push(
                    ErrorCode::InvalidExprShape,
                    "expr.chain items after first must be op",
                    item_path,
                );
            }
        }
    }
}

fn validate_chain_op(
    expr_op: &ExprOp,
    base_path: &str,
    produced_targets: &HashSet<Vec<PathToken>>,
    ctx: &mut ValidationCtx<'_>,
    scope: LocalScope,
) {
    if !is_valid_op(&expr_op.op) {
        ctx.push(
            ErrorCode::UnknownOp,
            "expr.op is not supported",
            format!("{}.op", base_path),
        );
    }

    validate_chain_op_args(expr_op, base_path, ctx);

    let expr_scope = element_expr_scope(&expr_op.op, true, expr_op.args.len(), scope);
    for (index, arg) in expr_op.args.iter().enumerate() {
        let arg_path = format!("{}.args[{}]", base_path, index);
        let arg_scope = match expr_scope {
            Some((expr_index, expr_scope)) if expr_index == index => expr_scope,
            _ => scope,
        };
        validate_expr(arg, &arg_path, produced_targets, ctx, arg_scope);
    }
}