1#![allow(non_snake_case)]23use jrsonnet_evaluator::{4 bail,5 function::{builtin, FuncVal, NativeFn},6 runtime_error,7 typed::{BoundedI32, BoundedUsize, Either2, FromUntyped},8 val::{equals, ArrValue, IndexableVal},9 Either, IStr, ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val,10};1112pub 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 bail!("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 func.evaluate_trivial().map_or_else(26 27 || Ok(ArrValue::range_exclusive(0, *sz).map(FromUntyped::from_untyped(Val::Func(func))?)),28 |trivial| {29 let mut out = Vec::with_capacity(*sz as usize);30 for _ in 0..*sz {31 out.push(trivial.clone());32 }33 Ok(ArrValue::eager(out))34 },35 )36}3738#[builtin]39pub fn builtin_repeat(what: Either![IStr, ArrValue], count: usize) -> Result<Val> {40 Ok(match what {41 Either2::A(s) => Val::string(s.repeat(count)),42 Either2::B(arr) => Val::Arr(43 ArrValue::repeated(arr, count)44 .ok_or_else(|| runtime_error!("repeated length overflow"))?,45 ),46 })47}4849#[builtin]50pub fn builtin_slice(51 indexable: IndexableVal,52 index: Option<Option<i32>>,53 end: Option<Option<i32>>,54 step: Option<Option<BoundedUsize<1, { i32::MAX as usize }>>>,55) -> Result<Val> {56 indexable57 .slice(index.flatten(), end.flatten(), step.flatten())58 .map(Val::from)59}6061#[builtin]62pub fn builtin_map(func: NativeFn!((Val) -> Val), arr: IndexableVal) -> ArrValue {63 let arr = arr.to_array();64 arr.map(func)65}6667#[builtin]68pub fn builtin_map_with_index(func: NativeFn!((u32, Val) -> Val), arr: IndexableVal) -> ArrValue {69 let arr = arr.to_array();70 arr.map_with_index(func)71}7273#[builtin]74pub fn builtin_map_with_key(75 func: NativeFn!((IStr, Val) -> Val),76 obj: ObjValue,77) -> Result<ObjValue> {78 let mut out = ObjValueBuilder::new();79 for (k, v) in obj.iter(80 81 82 83 #[cfg(feature = "exp-preserve-order")]84 true,85 ) {86 let v = v?;87 out.field(k.clone()).value(func.call(k, v)?);88 }89 Ok(out.build())90}9192#[builtin]93pub fn builtin_flatmap(94 func: NativeFn!((Either![String, Val]) -> Val),95 arr: IndexableVal,96) -> Result<IndexableVal> {97 use std::fmt::Write;98 match arr {99 IndexableVal::Str(str) => {100 let mut out = String::new();101 for c in str.chars() {102 match func.call(Either2::A(c.to_string()))? {103 Val::Str(o) => write!(out, "{o}").unwrap(),104 Val::Null => {}105 _ => bail!("in std.join all items should be strings"),106 }107 }108 Ok(IndexableVal::Str(out.into()))109 }110 IndexableVal::Arr(a) => {111 let mut out = Vec::new();112 for el in a.iter() {113 let el = el?;114 match func.call(Either2::B(el))? {115 Val::Arr(o) => {116 for oe in o.iter() {117 out.push(oe?);118 }119 }120 Val::Null => {}121 _ => bail!("in std.join all items should be arrays"),122 }123 }124 Ok(IndexableVal::Arr(out.into()))125 }126 }127}128129type FilterFunc = NativeFn!((Val) -> bool);130131#[builtin]132pub fn builtin_filter(func: FilterFunc, arr: ArrValue) -> Result<ArrValue> {133 arr.filter(|val| func.call(val.clone()))134}135136#[builtin]137pub fn builtin_filter_map(138 filter_func: FilterFunc,139 map_func: NativeFn!((Val) -> Val),140 arr: ArrValue,141) -> Result<ArrValue> {142 Ok(builtin_filter(filter_func, arr)?.map(map_func))143}144145#[builtin]146pub fn builtin_foldl(147 func: NativeFn!((Val, Either![Val, char]) -> Val),148 arr: Either![ArrValue, IStr],149 init: Val,150) -> Result<Val> {151 let mut acc = init;152 match arr {153 Either2::A(arr) => {154 for i in arr.iter() {155 acc = func.call(acc, Either2::A(i?))?;156 }157 }158 Either2::B(arr) => {159 for c in arr.chars() {160 acc = func.call(acc, Either2::B(c))?;161 }162 }163 }164 Ok(acc)165}166167#[builtin]168pub fn builtin_foldr(169 func: NativeFn!((Either![Val, char], Val) -> Val),170 arr: Either![ArrValue, IStr],171 init: Val,172) -> Result<Val> {173 let mut acc = init;174 match arr {175 Either2::A(arr) => {176 for i in arr.iter().rev() {177 acc = func.call(Either2::A(i?), acc)?;178 }179 }180 Either2::B(arr) => {181 for c in arr.chars().rev() {182 acc = func.call(Either2::B(c), acc)?;183 }184 }185 }186 Ok(acc)187}188189#[builtin]190pub fn builtin_range(from: i32, to: i32) -> Result<ArrValue> {191 if to < from {192 return Ok(ArrValue::empty());193 }194 Ok(ArrValue::range_inclusive(from, to))195}196197#[builtin]198pub fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {199 use std::fmt::Write;200 Ok(match sep {201 IndexableVal::Arr(joiner_items) => {202 let mut out = Vec::new();203204 let mut first = true;205 for item in arr.iter() {206 let item = item?.clone();207 if let Val::Arr(items) = item {208 if !first {209 out.reserve(joiner_items.len());210 211 for item in joiner_items.iter() {212 out.push(item?);213 }214 }215 first = false;216 out.reserve(items.len());217 for item in items.iter() {218 out.push(item?);219 }220 } else if matches!(item, Val::Null) {221 } else {222 bail!("in std.join all items should be arrays");223 }224 }225226 IndexableVal::Arr(out.into())227 }228 IndexableVal::Str(sep) => {229 let mut out = String::new();230231 let mut first = true;232 for item in arr.iter() {233 let item = item?.clone();234 if let Val::Str(item) = item {235 if !first {236 out += &sep;237 }238 first = false;239 write!(out, "{item}").unwrap();240 } else if matches!(item, Val::Null) {241 } else {242 bail!("in std.join all items should be strings");243 }244 }245246 IndexableVal::Str(out.into())247 }248 })249}250251#[builtin]252pub fn builtin_lines(arr: ArrValue) -> Result<IndexableVal> {253 builtin_join(254 IndexableVal::Str("\n".into()),255 ArrValue::extended(arr, ArrValue::eager(vec![Val::string("")])),256 )257}258259#[builtin]260pub fn builtin_resolve_path(f: String, r: String) -> String {261 let Some(pos) = f.rfind('/') else {262 return r;263 };264 format!("{}{}", &f[..=pos], r)265}266267pub fn deep_join_inner(out: &mut String, arr: IndexableVal) -> Result<()> {268 use std::fmt::Write;269 match arr {270 IndexableVal::Str(s) => write!(out, "{s}").expect("no error"),271 IndexableVal::Arr(arr) => {272 for ele in arr.iter() {273 let indexable = IndexableVal::from_untyped(ele?)?;274 deep_join_inner(out, indexable)?;275 }276 }277 }278 Ok(())279}280281#[builtin]282pub fn builtin_deep_join(arr: IndexableVal) -> Result<String> {283 let mut out = String::new();284 deep_join_inner(&mut out, arr)?;285 Ok(out)286}287288#[builtin]289pub fn builtin_reverse(arr: ArrValue) -> ArrValue {290 arr.reversed()291}292293#[builtin]294pub fn builtin_any(arr: ArrValue) -> Result<bool> {295 for v in arr.iter() {296 let v = bool::from_untyped(v?)?;297 if v {298 return Ok(true);299 }300 }301 Ok(false)302}303304#[builtin]305pub fn builtin_all(arr: ArrValue) -> Result<bool> {306 for v in arr.iter() {307 let v = bool::from_untyped(v?)?;308 if !v {309 return Ok(false);310 }311 }312 Ok(true)313}314315#[builtin]316pub fn builtin_member(arr: IndexableVal, x: Val) -> Result<bool> {317 match arr {318 IndexableVal::Str(str) => {319 let x: IStr = IStr::from_untyped(x)?;320 Ok(!x.is_empty() && str.contains(&*x))321 }322 IndexableVal::Arr(a) => {323 for item in a.iter() {324 let item = item?;325 if equals(&item, &x)? {326 return Ok(true);327 }328 }329 Ok(false)330 }331 }332}333334#[builtin]335pub fn builtin_find(value: Val, arr: ArrValue) -> Result<Vec<usize>> {336 let mut out = Vec::new();337 for (i, ele) in arr.iter().enumerate() {338 let ele = ele?;339 if equals(&ele, &value)? {340 out.push(i);341 }342 }343 Ok(out)344}345346#[builtin]347pub fn builtin_contains(arr: IndexableVal, elem: Val) -> Result<bool> {348 builtin_member(arr, elem)349}350351#[builtin]352pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {353 let mut count = 0;354 for item in arr.iter() {355 if equals(&item?, &x)? {356 count += 1;357 }358 }359 Ok(count)360}361362#[builtin]363pub fn builtin_avg(arr: Vec<f64>, onEmpty: Option<Thunk<Val>>) -> Result<Val> {364 if arr.is_empty() {365 return eval_on_empty(onEmpty);366 }367 Ok(Val::try_num(arr.iter().sum::<f64>() / (arr.len() as f64))?)368}369370#[builtin]371pub fn builtin_remove_at(arr: ArrValue, at: i32) -> Result<ArrValue> {372 let newArrLeft = arr.clone().slice(None, Some(at), None);373 let newArrRight = arr.slice(Some(at + 1), None, None);374375 Ok(ArrValue::extended(newArrLeft, newArrRight))376}377378#[builtin]379pub fn builtin_remove(arr: ArrValue, elem: Val) -> Result<ArrValue> {380 for (index, item) in arr.iter().enumerate() {381 if equals(&item?, &elem)? {382 return builtin_remove_at(arr.clone(), index as i32);383 }384 }385 Ok(arr)386}387388#[builtin]389pub fn builtin_flatten_arrays(arrs: Vec<ArrValue>) -> ArrValue {390 pub fn flatten_inner(values: &[ArrValue]) -> ArrValue {391 if values.len() == 1 {392 return values[0].clone();393 } else if values.len() == 2 {394 return ArrValue::extended(values[0].clone(), values[1].clone());395 }396 let (a, b) = values.split_at(values.len() / 2);397 ArrValue::extended(flatten_inner(a), flatten_inner(b))398 }399 if arrs.is_empty() {400 return ArrValue::empty();401 } else if arrs.len() == 1 {402 return arrs.into_iter().next().expect("single");403 }404 flatten_inner(&arrs)405}406407#[builtin]408pub fn builtin_flatten_deep_array(value: Val) -> Result<Vec<Val>> {409 fn process(value: Val, out: &mut Vec<Val>) -> Result<()> {410 match value {411 Val::Arr(arr) => {412 for ele in arr.iter() {413 process(ele?, out)?;414 }415 }416 _ => out.push(value),417 }418 Ok(())419 }420 let mut out = Vec::new();421 process(value, &mut out)?;422 Ok(out)423}424425#[builtin]426pub fn builtin_prune(427 a: Val,428429 #[default(false)]430 #[cfg(feature = "exp-preserve-order")]431 preserve_order: bool,432) -> Result<Val> {433 fn is_content(val: &Val) -> bool {434 match val {435 Val::Null => false,436 Val::Arr(a) => !a.is_empty(),437 Val::Obj(o) => !o.is_empty(),438 _ => true,439 }440 }441 Ok(match a {442 Val::Arr(a) => {443 let mut out = Vec::new();444 for (i, ele) in a.iter().enumerate() {445 let ele = ele446 .and_then(|v| {447 builtin_prune(448 v,449 #[cfg(feature = "exp-preserve-order")]450 preserve_order,451 )452 })453 .with_description(|| format!("elem <{i}> pruning"))?;454 if is_content(&ele) {455 out.push(ele);456 }457 }458 Val::Arr(ArrValue::eager(out))459 }460 Val::Obj(o) => {461 let mut out = ObjValueBuilder::new();462 for (name, value) in o.iter(463 #[cfg(feature = "exp-preserve-order")]464 preserve_order,465 ) {466 let value = value467 .and_then(|v| {468 builtin_prune(469 v,470 #[cfg(feature = "exp-preserve-order")]471 preserve_order,472 )473 })474 .with_description(|| format!("field <{name}> pruning"))?;475 if !is_content(&value) {476 continue;477 }478 out.field(name).value(value);479 }480 Val::Obj(out.build())481 }482 _ => a,483 })484}