datalogic-rs 5.1.0

High-performance JSONLogic (json-logic) rules engine and sandboxed expression evaluator in Rust — one core, official bindings for Node.js, WASM, Python, Go, Java, .NET, and PHP. Compile once, evaluate in nanoseconds.
Documentation
//! `min` and `max` — reductions over an array or variadic args. These are
//! "pipeline tops" that consume an array (typically produced by an upstream
//! filter/map). Arena wins:
//!   1. Input borrow: when args[0] is a root var, no clone of the input array.
//!   2. Composition: when args[0] is filter/map/all/some/none, the arena
//!      intermediate slice is consumed directly.
//!
//! Each op handles the SINGLE-ARG ARRAY form (e.g. `max(items)` over an array).
//! The multi-arg form (`max(a, b, c)`) is handled separately — it doesn't
//! involve array iteration.

use crate::arena::{ContextStack, DataValue};
use crate::operators::array::{IterArgKind, ResolvedInput, resolve_iter_input};
use crate::{CompiledNode, Engine, Result};
use bumpalo::Bump;

/// Generic helper for max/min over an arena-iterable input. `pick_better`
/// returns true when `candidate_f` should replace `best_f` (strictly better).
#[inline]
fn min_max<'a>(
    args: &'a [CompiledNode],
    iter_arg_kind: IterArgKind,
    ctx: &mut ContextStack<'a>,
    engine: &Engine,
    arena: &'a Bump,
    init: f64,
    pick_better: fn(f64, f64) -> bool,
) -> Result<&'a DataValue<'a>> {
    if args.is_empty() {
        return Err(crate::Error::invalid_args());
    }

    // Multi-arg variadic form: evaluate each arg, pick the best Number.
    if args.len() > 1 {
        return min_max_variadic(args, ctx, engine, arena, init, pick_better);
    }

    // Reject literal-array arg shape.
    if super::helpers::is_literal_array(&args[0]) {
        return Err(crate::Error::invalid_args());
    }

    let src = match resolve_iter_input(&args[0], iter_arg_kind, ctx, engine, arena)? {
        ResolvedInput::Iterable(s) => s,
        ResolvedInput::Empty => return Err(crate::Error::invalid_args()),
        ResolvedInput::Bridge(av) => {
            // Bridge is never Array/Null (see ResolvedInput::Bridge). A single
            // non-array arg must be a `Number`, returned unchanged.
            debug_assert!(!matches!(av, DataValue::Array(_) | DataValue::Null));
            if !matches!(av, DataValue::Number(_)) {
                return Err(crate::Error::invalid_args());
            }
            return Ok(av);
        }
    };

    if src.is_empty() {
        return Err(crate::Error::invalid_args());
    }

    let mut best_f = init;
    let mut best_idx: Option<usize> = None;
    let len = src.len();
    for i in 0..len {
        match src.get(i) {
            DataValue::Number(n) => {
                let f = n.as_f64();
                if pick_better(f, best_f) {
                    best_f = f;
                    best_idx = Some(i);
                }
            }
            _ => return Err(crate::Error::invalid_args()),
        }
    }

    match best_idx {
        // Re-borrow the arena value to preserve the original Number variant
        // (integer typing).
        Some(i) => Ok(src.get(i)),
        None => Ok(crate::arena::singletons::singleton_null()),
    }
}

#[inline]
fn min_max_variadic<'a>(
    args: &'a [CompiledNode],
    ctx: &mut ContextStack<'a>,
    engine: &Engine,
    arena: &'a Bump,
    init: f64,
    pick_better: fn(f64, f64) -> bool,
) -> Result<&'a DataValue<'a>> {
    let mut best_f = init;
    let mut best_av: Option<&'a DataValue<'a>> = None;
    for arg in args {
        let av = engine.dispatch_node(arg, ctx, arena)?;
        let f = match av {
            DataValue::Number(n) => n.as_f64(),
            _ => return Err(crate::Error::invalid_args()),
        };
        if pick_better(f, best_f) {
            best_f = f;
            best_av = Some(av);
        }
    }
    match best_av {
        Some(av) => Ok(av),
        None => Ok(crate::arena::singletons::singleton_null()),
    }
}

/// Arena-mode max(single_array_arg).
#[inline]
pub(crate) fn evaluate_max<'a>(
    args: &'a [CompiledNode],
    iter_arg_kind: IterArgKind,
    ctx: &mut ContextStack<'a>,
    engine: &Engine,
    arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
    min_max(
        args,
        iter_arg_kind,
        ctx,
        engine,
        arena,
        f64::NEG_INFINITY,
        |c, b| c > b,
    )
}

/// Arena-mode min(single_array_arg).
#[inline]
pub(crate) fn evaluate_min<'a>(
    args: &'a [CompiledNode],
    iter_arg_kind: IterArgKind,
    ctx: &mut ContextStack<'a>,
    engine: &Engine,
    arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
    min_max(
        args,
        iter_arg_kind,
        ctx,
        engine,
        arena,
        f64::INFINITY,
        |c, b| c < b,
    )
}