difftreelog
feat add std.avg
in: master
Upstream issue: https://github.com/google/jsonnet/pull/1087
3 files changed
crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth1use jrsonnet_evaluator::{2 error::{ErrorKind::RuntimeError, Result},3 function::{builtin, FuncVal},4 throw,5 typed::{BoundedI32, BoundedUsize, Either2, NativeFn, Typed},6 val::{equals, ArrValue, IndexableVal, StrValue},7 Either, IStr, Val,8};910#[builtin]11pub fn builtin_make_array(sz: BoundedI32<0, { i32::MAX }>, func: FuncVal) -> Result<ArrValue> {12 if *sz == 0 {13 return Ok(ArrValue::empty());14 }15 if let Some(trivial) = func.evaluate_trivial() {16 let mut out = Vec::with_capacity(*sz as usize);17 for _ in 0..*sz {18 out.push(trivial.clone())19 }20 Ok(ArrValue::eager(out))21 } else {22 Ok(ArrValue::range_exclusive(0, *sz).map(func))23 }24}2526#[builtin]27pub fn builtin_repeat(what: Either![IStr, ArrValue], count: usize) -> Result<Val> {28 Ok(match what {29 Either2::A(s) => Val::Str(StrValue::Flat(s.repeat(count).into())),30 Either2::B(arr) => Val::Arr(31 ArrValue::repeated(arr, count)32 .ok_or_else(|| RuntimeError("repeated length overflow".into()))?,33 ),34 })35}3637#[builtin]38pub fn builtin_slice(39 indexable: IndexableVal,40 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,41 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,42 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,43) -> Result<Val> {44 indexable.slice(index, end, step).map(Val::from)45}4647#[builtin]48pub fn builtin_map(func: FuncVal, arr: IndexableVal) -> ArrValue {49 let arr = arr.to_array();50 arr.map(func)51}5253#[builtin]54pub fn builtin_flatmap(55 func: NativeFn<((Either![String, Val],), Val)>,56 arr: IndexableVal,57) -> Result<IndexableVal> {58 use std::fmt::Write;59 match arr {60 IndexableVal::Str(str) => {61 let mut out = String::new();62 for c in str.chars() {63 match func(Either2::A(c.to_string()))? {64 Val::Str(o) => write!(out, "{o}").unwrap(),65 Val::Null => continue,66 _ => throw!("in std.join all items should be strings"),67 };68 }69 Ok(IndexableVal::Str(out.into()))70 }71 IndexableVal::Arr(a) => {72 let mut out = Vec::new();73 for el in a.iter() {74 let el = el?;75 match func(Either2::B(el))? {76 Val::Arr(o) => {77 for oe in o.iter() {78 out.push(oe?);79 }80 }81 Val::Null => continue,82 _ => throw!("in std.join all items should be arrays"),83 };84 }85 Ok(IndexableVal::Arr(out.into()))86 }87 }88}8990#[builtin]91pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {92 arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(val.clone(),), false)?))93}9495#[builtin]96pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {97 let mut acc = init;98 for i in arr.iter() {99 acc = func.evaluate_simple(&(acc, i?), false)?;100 }101 Ok(acc)102}103104#[builtin]105pub fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {106 let mut acc = init;107 for i in arr.iter().rev() {108 acc = func.evaluate_simple(&(i?, acc), false)?;109 }110 Ok(acc)111}112113#[builtin]114pub fn builtin_range(from: i32, to: i32) -> Result<ArrValue> {115 if to < from {116 return Ok(ArrValue::empty());117 }118 Ok(ArrValue::range_inclusive(from, to))119}120121#[builtin]122pub fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {123 use std::fmt::Write;124 Ok(match sep {125 IndexableVal::Arr(joiner_items) => {126 let mut out = Vec::new();127128 let mut first = true;129 for item in arr.iter() {130 let item = item?.clone();131 if let Val::Arr(items) = item {132 if !first {133 out.reserve(joiner_items.len());134 // TODO: extend135 for item in joiner_items.iter() {136 out.push(item?);137 }138 }139 first = false;140 out.reserve(items.len());141 for item in items.iter() {142 out.push(item?);143 }144 } else if matches!(item, Val::Null) {145 continue;146 } else {147 throw!("in std.join all items should be arrays");148 }149 }150151 IndexableVal::Arr(out.into())152 }153 IndexableVal::Str(sep) => {154 let mut out = String::new();155156 let mut first = true;157 for item in arr.iter() {158 let item = item?.clone();159 if let Val::Str(item) = item {160 if !first {161 out += &sep;162 }163 first = false;164 write!(out, "{item}").unwrap()165 } else if matches!(item, Val::Null) {166 continue;167 } else {168 throw!("in std.join all items should be strings");169 }170 }171172 IndexableVal::Str(out.into())173 }174 })175}176177#[builtin]178pub fn builtin_reverse(arr: ArrValue) -> ArrValue {179 arr.reversed()180}181182#[builtin]183pub fn builtin_any(arr: ArrValue) -> Result<bool> {184 for v in arr.iter() {185 let v = bool::from_untyped(v?)?;186 if v {187 return Ok(true);188 }189 }190 Ok(false)191}192193#[builtin]194pub fn builtin_all(arr: ArrValue) -> Result<bool> {195 for v in arr.iter() {196 let v = bool::from_untyped(v?)?;197 if !v {198 return Ok(false);199 }200 }201 Ok(true)202}203204#[builtin]205pub fn builtin_member(arr: IndexableVal, x: Val) -> Result<bool> {206 match arr {207 IndexableVal::Str(str) => {208 let x: IStr = IStr::from_untyped(x)?;209 Ok(!x.is_empty() && str.contains(&*x))210 }211 IndexableVal::Arr(a) => {212 for item in a.iter() {213 let item = item?;214 if equals(&item, &x)? {215 return Ok(true);216 }217 }218 Ok(false)219 }220 }221}222223#[builtin]224pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {225 let mut count = 0;226 for item in arr.iter() {227 if equals(&item?, &x)? {228 count += 1;229 }230 }231 Ok(count)232}1#![allow(non_snake_case)]23use jrsonnet_evaluator::{4 error::{ErrorKind::RuntimeError, Result},5 function::{builtin, FuncVal},6 throw,7 typed::{BoundedI32, BoundedUsize, Either2, NativeFn, Typed},8 val::{equals, ArrValue, IndexableVal, StrValue},9 Either, IStr, Thunk, Val,10};1112pub(crate) fn eval_on_empty(on_empty: Option<Thunk<Val>>) -> Result<Val> {13 if let Some(on_empty) = on_empty {14 on_empty.evaluate()15 } else {16 throw!("expected non-empty array")17 }18}1920#[builtin]21pub fn builtin_make_array(sz: BoundedI32<0, { i32::MAX }>, func: FuncVal) -> Result<ArrValue> {22 if *sz == 0 {23 return Ok(ArrValue::empty());24 }25 if let Some(trivial) = func.evaluate_trivial() {26 let mut out = Vec::with_capacity(*sz as usize);27 for _ in 0..*sz {28 out.push(trivial.clone())29 }30 Ok(ArrValue::eager(out))31 } else {32 Ok(ArrValue::range_exclusive(0, *sz).map(func))33 }34}3536#[builtin]37pub fn builtin_repeat(what: Either![IStr, ArrValue], count: usize) -> Result<Val> {38 Ok(match what {39 Either2::A(s) => Val::Str(StrValue::Flat(s.repeat(count).into())),40 Either2::B(arr) => Val::Arr(41 ArrValue::repeated(arr, count)42 .ok_or_else(|| RuntimeError("repeated length overflow".into()))?,43 ),44 })45}4647#[builtin]48pub fn builtin_slice(49 indexable: IndexableVal,50 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,51 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,52 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,53) -> Result<Val> {54 indexable.slice(index, end, step).map(Val::from)55}5657#[builtin]58pub fn builtin_map(func: FuncVal, arr: IndexableVal) -> ArrValue {59 let arr = arr.to_array();60 arr.map(func)61}6263#[builtin]64pub fn builtin_flatmap(65 func: NativeFn<((Either![String, Val],), Val)>,66 arr: IndexableVal,67) -> Result<IndexableVal> {68 use std::fmt::Write;69 match arr {70 IndexableVal::Str(str) => {71 let mut out = String::new();72 for c in str.chars() {73 match func(Either2::A(c.to_string()))? {74 Val::Str(o) => write!(out, "{o}").unwrap(),75 Val::Null => continue,76 _ => throw!("in std.join all items should be strings"),77 };78 }79 Ok(IndexableVal::Str(out.into()))80 }81 IndexableVal::Arr(a) => {82 let mut out = Vec::new();83 for el in a.iter() {84 let el = el?;85 match func(Either2::B(el))? {86 Val::Arr(o) => {87 for oe in o.iter() {88 out.push(oe?);89 }90 }91 Val::Null => continue,92 _ => throw!("in std.join all items should be arrays"),93 };94 }95 Ok(IndexableVal::Arr(out.into()))96 }97 }98}99100#[builtin]101pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {102 arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(val.clone(),), false)?))103}104105#[builtin]106pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {107 let mut acc = init;108 for i in arr.iter() {109 acc = func.evaluate_simple(&(acc, i?), false)?;110 }111 Ok(acc)112}113114#[builtin]115pub fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {116 let mut acc = init;117 for i in arr.iter().rev() {118 acc = func.evaluate_simple(&(i?, acc), false)?;119 }120 Ok(acc)121}122123#[builtin]124pub fn builtin_range(from: i32, to: i32) -> Result<ArrValue> {125 if to < from {126 return Ok(ArrValue::empty());127 }128 Ok(ArrValue::range_inclusive(from, to))129}130131#[builtin]132pub fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {133 use std::fmt::Write;134 Ok(match sep {135 IndexableVal::Arr(joiner_items) => {136 let mut out = Vec::new();137138 let mut first = true;139 for item in arr.iter() {140 let item = item?.clone();141 if let Val::Arr(items) = item {142 if !first {143 out.reserve(joiner_items.len());144 // TODO: extend145 for item in joiner_items.iter() {146 out.push(item?);147 }148 }149 first = false;150 out.reserve(items.len());151 for item in items.iter() {152 out.push(item?);153 }154 } else if matches!(item, Val::Null) {155 continue;156 } else {157 throw!("in std.join all items should be arrays");158 }159 }160161 IndexableVal::Arr(out.into())162 }163 IndexableVal::Str(sep) => {164 let mut out = String::new();165166 let mut first = true;167 for item in arr.iter() {168 let item = item?.clone();169 if let Val::Str(item) = item {170 if !first {171 out += &sep;172 }173 first = false;174 write!(out, "{item}").unwrap()175 } else if matches!(item, Val::Null) {176 continue;177 } else {178 throw!("in std.join all items should be strings");179 }180 }181182 IndexableVal::Str(out.into())183 }184 })185}186187#[builtin]188pub fn builtin_reverse(arr: ArrValue) -> ArrValue {189 arr.reversed()190}191192#[builtin]193pub fn builtin_any(arr: ArrValue) -> Result<bool> {194 for v in arr.iter() {195 let v = bool::from_untyped(v?)?;196 if v {197 return Ok(true);198 }199 }200 Ok(false)201}202203#[builtin]204pub fn builtin_all(arr: ArrValue) -> Result<bool> {205 for v in arr.iter() {206 let v = bool::from_untyped(v?)?;207 if !v {208 return Ok(false);209 }210 }211 Ok(true)212}213214#[builtin]215pub fn builtin_member(arr: IndexableVal, x: Val) -> Result<bool> {216 match arr {217 IndexableVal::Str(str) => {218 let x: IStr = IStr::from_untyped(x)?;219 Ok(!x.is_empty() && str.contains(&*x))220 }221 IndexableVal::Arr(a) => {222 for item in a.iter() {223 let item = item?;224 if equals(&item, &x)? {225 return Ok(true);226 }227 }228 Ok(false)229 }230 }231}232233#[builtin]234pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {235 let mut count = 0;236 for item in arr.iter() {237 if equals(&item?, &x)? {238 count += 1;239 }240 }241 Ok(count)242}243244#[builtin]245pub fn builtin_avg(arr: Vec<f64>, onEmpty: Option<Thunk<Val>>) -> Result<Val> {246 if arr.is_empty() {247 return eval_on_empty(onEmpty);248 }249 Ok(Val::Num(arr.iter().sum::<f64>() / (arr.len() as f64)))250}crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -81,6 +81,7 @@
("all", builtin_all::INST),
("member", builtin_member::INST),
("count", builtin_count::INST),
+ ("avg", builtin_avg::INST),
// Math
("abs", builtin_abs::INST),
("sign", builtin_sign::INST),
crates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/sort.rs
+++ b/crates/jrsonnet-stdlib/src/sort.rs
@@ -13,6 +13,8 @@
use jrsonnet_gcmodule::Cc;
use jrsonnet_parser::BinaryOpType;
+use crate::eval_on_empty;
+
#[derive(Copy, Clone)]
enum SortKeyType {
Number,
@@ -207,13 +209,6 @@
}
}
-fn eval_on_empty(on_empty: Option<Thunk<Val>>) -> Result<Val> {
- if let Some(on_empty) = on_empty {
- on_empty.evaluate()
- } else {
- throw!("expected non-empty array")
- }
-}
fn eval_keyf(val: Val, key_f: &Option<FuncVal>) -> Result<Val> {
if let Some(key_f) = key_f {