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 145 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_contains(arr: IndexableVal, elem: Val) -> Result<bool> {235 builtin_member(arr, elem)236}237238#[builtin]239pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {240 let mut count = 0;241 for item in arr.iter() {242 if equals(&item?, &x)? {243 count += 1;244 }245 }246 Ok(count)247}248249#[builtin]250pub fn builtin_avg(arr: Vec<f64>, onEmpty: Option<Thunk<Val>>) -> Result<Val> {251 if arr.is_empty() {252 return eval_on_empty(onEmpty);253 }254 Ok(Val::Num(arr.iter().sum::<f64>() / (arr.len() as f64)))255}