From 54e5a6e38415dbe114399f39754a8c177f9b32d7 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Wed, 14 Jun 2023 19:24:16 +0000 Subject: [PATCH] feat: add std.minArray/std.maxArray Upstream issue: https://github.com/google/jsonnet/pull/1081 Upstream issue: https://github.com/google/jsonnet/pull/1074 --- --- a/crates/jrsonnet-stdlib/src/lib.rs +++ b/crates/jrsonnet-stdlib/src/lib.rs @@ -112,6 +112,8 @@ ("sort", builtin_sort::INST), ("uniq", builtin_uniq::INST), ("set", builtin_set::INST), + ("minArray", builtin_min_array::INST), + ("maxArray", builtin_max_array::INST), // Hash ("md5", builtin_md5::INST), ("sha256", builtin_sha256::INST), --- a/crates/jrsonnet-stdlib/src/sort.rs +++ b/crates/jrsonnet-stdlib/src/sort.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case)] + use std::cmp::Ordering; use jrsonnet_evaluator::{ @@ -9,6 +11,7 @@ Thunk, Val, }; use jrsonnet_gcmodule::Cc; +use jrsonnet_parser::BinaryOpType; #[derive(Copy, Clone)] enum SortKeyType { @@ -64,15 +67,13 @@ let mut err = None; // evaluate_compare_op will never return equal on types, which are different from // jsonnet perspective - values.sort_unstable_by(|a, b| { - match evaluate_compare_op(a, b, jrsonnet_parser::BinaryOpType::Lt) { - Ok(ord) => ord, - Err(e) if err.is_none() => { - let _ = err.insert(e); - Ordering::Equal - } - Err(_) => Ordering::Equal, + values.sort_unstable_by(|a, b| match evaluate_compare_op(a, b, BinaryOpType::Lt) { + Ok(ord) => ord, + Err(e) if err.is_none() => { + let _ = err.insert(e); + Ordering::Equal } + Err(_) => Ordering::Equal, }); if let Some(err) = err { return Err(err); @@ -105,16 +106,16 @@ let mut err = None; // evaluate_compare_op will never return equal on types, which are different from // jsonnet perspective - vk.sort_by(|(_a, ak), (_b, bk)| { - match evaluate_compare_op(ak, bk, jrsonnet_parser::BinaryOpType::Lt) { + vk.sort_by( + |(_a, ak), (_b, bk)| match evaluate_compare_op(ak, bk, BinaryOpType::Lt) { Ok(ord) => ord, Err(e) if err.is_none() => { let _ = err.insert(e); Ordering::Equal } Err(_) => Ordering::Equal, - } - }); + }, + ); if let Some(err) = err { return Err(err); } @@ -138,7 +139,6 @@ } #[builtin] -#[allow(non_snake_case)] pub fn builtin_sort(arr: ArrValue, keyF: Option) -> Result { super::sort::sort(arr, keyF.unwrap_or_else(FuncVal::identity)) } @@ -206,3 +206,57 @@ Ok(ArrValue::lazy(Cc::new(arr))) } } + +fn eval_on_empty(on_empty: Option>) -> Result { + if let Some(on_empty) = on_empty { + on_empty.evaluate() + } else { + throw!("expected non-empty array") + } +} + +fn eval_keyf(val: Val, key_f: &Option) -> Result { + if let Some(key_f) = key_f { + key_f.evaluate_simple(&(val,), false) + } else { + Ok(val) + } +} + +fn array_top1(arr: ArrValue, key_f: Option, ordering: Ordering) -> Result { + let mut iter = arr.iter(); + let mut min = iter.next().expect("not empty")?; + let mut min_key = eval_keyf(min.clone(), &key_f)?; + for item in iter { + let cur = item?; + let cur_key = eval_keyf(cur.clone(), &key_f)?; + if evaluate_compare_op(&cur_key, &min_key, BinaryOpType::Lt)? == ordering { + min = cur; + min_key = cur_key; + } + } + Ok(min) +} + +#[builtin] +pub fn builtin_min_array( + arr: ArrValue, + keyF: Option, + onEmpty: Option>, +) -> Result { + if arr.is_empty() { + return eval_on_empty(onEmpty); + } + array_top1(arr, keyF, Ordering::Less) +} +#[builtin] +pub fn builtin_max_array( + arr: ArrValue, + keyF: Option, + onEmpty: Option>, +) -> Result { + if arr.is_empty() { + return eval_on_empty(onEmpty); + } + array_top1(arr, keyF, Ordering::Greater) +} -- gitstuff