skillet 0.6.2

Skillet: micro expression language (arithmetic, logical, functions, arrays, conditionals, excel formulas) made in Rust bin cli and server
Documentation
use crate::types::Value;
use crate::error::Error;

pub fn exec_arithmetic(name: &str, args: &[Value]) -> Result<Value, Error> {
    match name {
        "SUM" => {
            let mut acc = 0.0;
            fn sum_value(v: &Value, acc: &mut f64) {
                match v {
                    Value::Number(n) => *acc += *n,
                    Value::Array(items) => {
                        for it in items { sum_value(it, acc); }
                    }
                    Value::Boolean(_) => {}
                    Value::String(_) => {}
                    Value::Null => {}
                    Value::Currency(n) => *acc += *n,
                    Value::DateTime(_) => {}
                    Value::Json(_) => {}
                }
            }
            for a in args { sum_value(a, &mut acc); }
            Ok(Value::Number(acc))
        }
        "ROUND" => {
            if args.is_empty() { return Ok(Value::Number(0.0)); }
            let n = match args[0] { Value::Number(n) => n, _ => return Err(Error::new("ROUND expects number", None)) };
            let decimals = if args.len() > 1 { match args[1] { Value::Number(d) => d as i32, _ => 0 } } else { 0 };
            let factor = 10f64.powi(decimals.max(0));
            Ok(Value::Number((n * factor).round() / factor))
        }
        "CEIL" => {
            let n = match args.get(0) { Some(Value::Number(n)) => *n, _ => 0.0 };
            Ok(Value::Number(n.ceil()))
        }
        "FLOOR" => {
            let n = match args.get(0) { Some(Value::Number(n)) => *n, _ => 0.0 };
            Ok(Value::Number(n.floor()))
        }
        "ABS" => {
            let n = match args.get(0) { Some(Value::Number(n)) => *n, _ => 0.0 };
            Ok(Value::Number(n.abs()))
        }
        "SQRT" => {
            let n = match args.get(0) { Some(Value::Number(n)) => *n, _ => 0.0 };
            Ok(Value::Number(n.sqrt()))
        }
        "POW" | "POWER" => {
            let a = match args.get(0) { Some(Value::Number(n)) => *n, _ => 0.0 };
            let b = match args.get(1) { Some(Value::Number(n)) => *n, _ => 0.0 };
            Ok(Value::Number(a.powf(b)))
        }
        "MOD" => {
            let a = match args.get(0) { Some(Value::Number(n)) => *n, _ => 0.0 };
            let b = match args.get(1) { Some(Value::Number(n)) => *n, _ => 1.0 };
            Ok(Value::Number(a % b))
        }
        "INT" => {
            let n = match args.get(0) { Some(Value::Number(n)) => *n, _ => 0.0 };
            Ok(Value::Number(n.floor()))
        }
        "CEILING" => {
            let n = match args.get(0) { Some(Value::Number(n)) => *n, _ => 0.0 };
            let _significance = match args.get(1) { Some(Value::Number(s)) => *s, _ => 1.0 };
            Ok(Value::Number(n.ceil()))
        }
        "AVG" | "AVERAGE" => {
            let mut acc = 0.0;
            let mut count = 0usize;
            fn visit(v: &Value, acc: &mut f64, count: &mut usize) {
                match v {
                    Value::Number(n) => { *acc += *n; *count += 1; }
                    Value::Array(items) => for it in items { visit(it, acc, count); },
                    Value::Boolean(_) => {}
                    Value::String(_) => {}
                    Value::Null => {}
                    Value::Currency(n) => { *acc += *n; *count += 1; }
                    Value::DateTime(_) => {}
                    Value::Json(_) => {}
                }
            }
            for a in args { visit(a, &mut acc, &mut count); }
            let avg = if count == 0 { 0.0 } else { acc / count as f64 };
            Ok(Value::Number(avg))
        }
        "MIN" => {
            let mut cur: Option<f64> = None;
            fn visit(v: &Value, cur: &mut Option<f64>) {
                match v {
                    Value::Number(n) => { *cur = Some(cur.map_or(*n, |c| c.min(*n))); }
                    Value::Array(items) => for it in items { visit(it, cur); },
                    Value::Boolean(_) => {}
                    Value::String(_) => {}
                    Value::Null => {}
                    Value::Currency(n) => { *cur = Some(cur.map_or(*n, |c| c.min(*n))); }
                    Value::DateTime(_) => {}
                    Value::Json(_) => {}
                }
            }
            for a in args { visit(a, &mut cur); }
            Ok(Value::Number(cur.unwrap_or(0.0)))
        }
        "MAX" => {
            let mut cur: Option<f64> = None;
            fn visit(v: &Value, cur: &mut Option<f64>) {
                match v {
                    Value::Number(n) => { *cur = Some(cur.map_or(*n, |c| c.max(*n))); }
                    Value::Array(items) => for it in items { visit(it, cur); },
                    Value::Boolean(_) => {}
                    Value::String(_) => {}
                    Value::Null => {}
                    Value::Currency(n) => { *cur = Some(cur.map_or(*n, |c| c.max(*n))); }
                    Value::DateTime(_) => {}
                    Value::Json(_) => {}
                }
            }
            for a in args { visit(a, &mut cur); }
            Ok(Value::Number(cur.unwrap_or(0.0)))
        }
        "PRODUCT" | "MULTIPLY" => {
            let mut acc = 1.0;
            fn multiply_value(v: &Value, acc: &mut f64) {
                match v {
                    Value::Number(n) => *acc *= *n,
                    Value::Array(items) => {
                        for it in items { multiply_value(it, acc); }
                    }
                    Value::Boolean(_) => {}
                    Value::String(_) => {}
                    Value::Null => {}
                    Value::Currency(n) => *acc *= *n,
                    Value::DateTime(_) => {}
                    Value::Json(_) => {}
                }
            }
            for a in args { multiply_value(a, &mut acc); }
            Ok(Value::Number(acc))
        }
        _ => Err(Error::new(format!("Unknown arithmetic function: {}", name), None)),
    }
}