1#![allow(non_snake_case)]23use jrsonnet_evaluator::{4 Either, IStr, ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val, bail,5 function::{FuncVal, NativeFn, builtin},6 runtime_error,7 typed::{BoundedI32, BoundedUsize, Either2, FromUntyped},8 val::{ArrValue, IndexableVal, equals},9};1011pub fn eval_on_empty(on_empty: Option<Thunk<Val>>) -> Result<Val> {12 if let Some(on_empty) = on_empty {13 on_empty.evaluate()14 } else {15 bail!("expected non-empty array")16 }17}1819#[builtin]20pub fn builtin_make_array(sz: BoundedI32<0, { i32::MAX }>, func: FuncVal) -> Result<ArrValue> {21 if *sz == 0 {22 return Ok(ArrValue::empty());23 }24 func.evaluate_trivial().map_or_else(25 26 || Ok(ArrValue::range_exclusive(0, *sz).map(FromUntyped::from_untyped(Val::Func(func))?)),27 |trivial| {28 let mut out = Vec::with_capacity(*sz as usize);29 for _ in 0..*sz {30 out.push(trivial.clone());31 }32 Ok(ArrValue::eager(out))33 },34 )35}3637#[builtin]38pub fn builtin_repeat(what: Either![IStr, ArrValue], count: usize) -> Result<Val> {39 Ok(match what {40 Either2::A(s) => Val::string(s.repeat(count)),41 Either2::B(arr) => Val::Arr(42 ArrValue::repeated(arr, count)43 .ok_or_else(|| runtime_error!("repeated length overflow"))?,44 ),45 })46}4748#[builtin]49pub fn builtin_slice(50 indexable: IndexableVal,51 index: Option<Option<i32>>,52 end: Option<Option<i32>>,53 step: Option<Option<BoundedUsize<1, { i32::MAX as usize }>>>,54) -> Result<Val> {55 indexable56 .slice(index.flatten(), end.flatten(), step.flatten())57 .map(Val::from)58}5960#[builtin]61pub fn builtin_map(func: NativeFn!((Val) -> Val), arr: IndexableVal) -> ArrValue {62 let arr = arr.to_array();63 arr.map(func)64}6566#[builtin]67pub fn builtin_map_with_index(func: NativeFn!((u32, Val) -> Val), arr: IndexableVal) -> ArrValue {68 let arr = arr.to_array();69 arr.map_with_index(func)70}7172#[builtin]73pub fn builtin_map_with_key(74 func: NativeFn!((IStr, Val) -> Val),75 obj: ObjValue,76) -> Result<ObjValue> {77 let mut out = ObjValueBuilder::new();78 for (k, v) in obj.iter(79 80 81 82 #[cfg(feature = "exp-preserve-order")]83 true,84 ) {85 let v = v?;86 out.field(k.clone()).value(func.call(k, v)?);87 }88 Ok(out.build())89}9091#[builtin]92pub fn builtin_flatmap(93 func: NativeFn!((Either![String, Val]) -> Val),94 arr: IndexableVal,95) -> Result<IndexableVal> {96 use std::fmt::Write;97 match arr {98 IndexableVal::Str(str) => {99 let mut out = String::new();100 for c in str.chars() {101 match func.call(Either2::A(c.to_string()))? {102 Val::Str(o) => write!(out, "{o}").unwrap(),103 Val::Null => {}104 _ => bail!("in std.join all items should be strings"),105 }106 }107 Ok(IndexableVal::Str(out.into()))108 }109 IndexableVal::Arr(a) => {110 let mut out = Vec::new();111 for el in a.iter() {112 let el = el?;113 match func.call(Either2::B(el))? {114 Val::Arr(o) => {115 for oe in o.iter() {116 out.push(oe?);117 }118 }119 Val::Null => {}120 _ => bail!("in std.join all items should be arrays"),121 }122 }123 Ok(IndexableVal::Arr(out.into()))124 }125 }126}127128type FilterFunc = NativeFn!((Thunk<Val>) -> bool);129130#[builtin]131pub fn builtin_filter(func: FilterFunc, arr: ArrValue) -> Result<ArrValue> {132 arr.filter(func)133}134135#[builtin]136pub fn builtin_filter_map(137 filter_func: FilterFunc,138 map_func: NativeFn!((Val) -> Val),139 arr: ArrValue,140) -> Result<ArrValue> {141 Ok(arr.filter(filter_func)?.map(map_func))142}143144#[builtin]145pub fn builtin_foldl(146 func: NativeFn!((Val, Either![Val, char]) -> Val),147 arr: Either![ArrValue, IStr],148 init: Val,149) -> Result<Val> {150 let mut acc = init;151 match arr {152 Either2::A(arr) => {153 for i in arr.iter() {154 acc = func.call(acc, Either2::A(i?))?;155 }156 }157 Either2::B(arr) => {158 for c in arr.chars() {159 acc = func.call(acc, Either2::B(c))?;160 }161 }162 }163 Ok(acc)164}165166#[builtin]167pub fn builtin_foldr(168 func: NativeFn!((Either![Val, char], Val) -> Val),169 arr: Either![ArrValue, IStr],170 init: Val,171) -> Result<Val> {172 let mut acc = init;173 match arr {174 Either2::A(arr) => {175 for i in arr.iter().rev() {176 acc = func.call(Either2::A(i?), acc)?;177 }178 }179 Either2::B(arr) => {180 for c in arr.chars().rev() {181 acc = func.call(Either2::B(c), acc)?;182 }183 }184 }185 Ok(acc)186}187188#[builtin]189pub fn builtin_range(from: i32, to: i32) -> Result<ArrValue> {190 if to < from {191 return Ok(ArrValue::empty());192 }193 Ok(ArrValue::range_inclusive(from, to))194}195196#[builtin]197pub fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {198 use std::fmt::Write;199 Ok(match sep {200 IndexableVal::Arr(joiner_items) => {201 let mut out = Vec::new();202203 let mut first = true;204 for item in arr.iter() {205 let item = item?.clone();206 if let Val::Arr(items) = item {207 if !first {208 out.reserve(joiner_items.len());209 210 for item in joiner_items.iter() {211 out.push(item?);212 }213 }214 first = false;215 out.reserve(items.len());216 for item in items.iter() {217 out.push(item?);218 }219 } else if matches!(item, Val::Null) {220 } else {221 bail!("in std.join all items should be arrays");222 }223 }224225 IndexableVal::Arr(out.into())226 }227 IndexableVal::Str(sep) => {228 let mut out = String::new();229230 let mut first = true;231 for item in arr.iter() {232 let item = item?.clone();233 if let Val::Str(item) = item {234 if !first {235 out += &sep;236 }237 first = false;238 write!(out, "{item}").unwrap();239 } else if matches!(item, Val::Null) {240 } else {241 bail!("in std.join all items should be strings");242 }243 }244245 IndexableVal::Str(out.into())246 }247 })248}249250#[builtin]251pub fn builtin_lines(arr: ArrValue) -> Result<IndexableVal> {252 builtin_join(253 IndexableVal::Str("\n".into()),254 ArrValue::extended(arr, ArrValue::eager(vec![Val::string("")])),255 )256}257258#[builtin]259pub fn builtin_resolve_path(f: String, r: String) -> String {260 let Some(pos) = f.rfind('/') else {261 return r;262 };263 format!("{}{}", &f[..=pos], r)264}265266pub fn deep_join_inner(out: &mut String, arr: IndexableVal) -> Result<()> {267 use std::fmt::Write;268 match arr {269 IndexableVal::Str(s) => write!(out, "{s}").expect("no error"),270 IndexableVal::Arr(arr) => {271 for ele in arr.iter() {272 let indexable = IndexableVal::from_untyped(ele?)?;273 deep_join_inner(out, indexable)?;274 }275 }276 }277 Ok(())278}279280#[builtin]281pub fn builtin_deep_join(arr: IndexableVal) -> Result<String> {282 let mut out = String::new();283 deep_join_inner(&mut out, arr)?;284 Ok(out)285}286287#[builtin]288pub fn builtin_reverse(arr: ArrValue) -> ArrValue {289 arr.reversed()290}291292#[builtin]293pub fn builtin_any(arr: ArrValue) -> Result<bool> {294 for v in arr.iter() {295 let v = bool::from_untyped(v?)?;296 if v {297 return Ok(true);298 }299 }300 Ok(false)301}302303#[builtin]304pub fn builtin_all(arr: ArrValue) -> Result<bool> {305 for v in arr.iter() {306 let v = bool::from_untyped(v?)?;307 if !v {308 return Ok(false);309 }310 }311 Ok(true)312}313314#[builtin]315pub fn builtin_member(arr: IndexableVal, x: Val) -> Result<bool> {316 match arr {317 IndexableVal::Str(str) => {318 let x: IStr = IStr::from_untyped(x)?;319 Ok(!x.is_empty() && str.contains(&*x))320 }321 IndexableVal::Arr(a) => {322 for item in a.iter() {323 let item = item?;324 if equals(&item, &x)? {325 return Ok(true);326 }327 }328 Ok(false)329 }330 }331}332333#[builtin]334pub fn builtin_find(value: Val, arr: ArrValue) -> Result<Vec<usize>> {335 let mut out = Vec::new();336 for (i, ele) in arr.iter().enumerate() {337 let ele = ele?;338 if equals(&ele, &value)? {339 out.push(i);340 }341 }342 Ok(out)343}344345#[builtin]346pub fn builtin_contains(arr: IndexableVal, elem: Val) -> Result<bool> {347 builtin_member(arr, elem)348}349350#[builtin]351pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {352 let mut count = 0;353 for item in arr.iter() {354 if equals(&item?, &x)? {355 count += 1;356 }357 }358 Ok(count)359}360361#[builtin]362pub fn builtin_avg(arr: Vec<f64>, onEmpty: Option<Thunk<Val>>) -> Result<Val> {363 if arr.is_empty() {364 return eval_on_empty(onEmpty);365 }366 Ok(Val::try_num(arr.iter().sum::<f64>() / (arr.len() as f64))?)367}368369#[builtin]370pub fn builtin_remove_at(arr: ArrValue, at: i32) -> Result<ArrValue> {371 let newArrLeft = arr.clone().slice(None, Some(at), None);372 let newArrRight = arr.slice(Some(at + 1), None, None);373374 Ok(ArrValue::extended(newArrLeft, newArrRight))375}376377#[builtin]378pub fn builtin_remove(arr: ArrValue, elem: Val) -> Result<ArrValue> {379 for (index, item) in arr.iter().enumerate() {380 if equals(&item?, &elem)? {381 return builtin_remove_at(arr.clone(), index as i32);382 }383 }384 Ok(arr)385}386387#[builtin]388pub fn builtin_flatten_arrays(arrs: Vec<ArrValue>) -> ArrValue {389 pub fn flatten_inner(values: &[ArrValue]) -> ArrValue {390 if values.len() == 1 {391 return values[0].clone();392 } else if values.len() == 2 {393 return ArrValue::extended(values[0].clone(), values[1].clone());394 }395 let (a, b) = values.split_at(values.len() / 2);396 ArrValue::extended(flatten_inner(a), flatten_inner(b))397 }398 if arrs.is_empty() {399 return ArrValue::empty();400 } else if arrs.len() == 1 {401 return arrs.into_iter().next().expect("single");402 }403 flatten_inner(&arrs)404}405406#[builtin]407pub fn builtin_flatten_deep_array(value: Val) -> Result<Vec<Val>> {408 fn process(value: Val, out: &mut Vec<Val>) -> Result<()> {409 match value {410 Val::Arr(arr) => {411 for ele in arr.iter() {412 process(ele?, out)?;413 }414 }415 _ => out.push(value),416 }417 Ok(())418 }419 let mut out = Vec::new();420 process(value, &mut out)?;421 Ok(out)422}423424#[builtin]425pub fn builtin_prune(426 a: Val,427428 #[default(false)]429 #[cfg(feature = "exp-preserve-order")]430 preserve_order: bool,431) -> Result<Val> {432 fn is_content(val: &Val) -> bool {433 match val {434 Val::Null => false,435 Val::Arr(a) => !a.is_empty(),436 Val::Obj(o) => !o.is_empty(),437 _ => true,438 }439 }440 Ok(match a {441 Val::Arr(a) => {442 let mut out = Vec::new();443 for (i, ele) in a.iter().enumerate() {444 let ele = ele445 .and_then(|v| {446 builtin_prune(447 v,448 #[cfg(feature = "exp-preserve-order")]449 preserve_order,450 )451 })452 .with_description(|| format!("elem <{i}> pruning"))?;453 if is_content(&ele) {454 out.push(ele);455 }456 }457 Val::Arr(ArrValue::eager(out))458 }459 Val::Obj(o) => {460 let mut out = ObjValueBuilder::new();461 for (name, value) in o.iter(462 #[cfg(feature = "exp-preserve-order")]463 preserve_order,464 ) {465 let value = value466 .and_then(|v| {467 builtin_prune(468 v,469 #[cfg(feature = "exp-preserve-order")]470 preserve_order,471 )472 })473 .with_description(|| format!("field <{name}> pruning"))?;474 if !is_content(&value) {475 continue;476 }477 out.field(name).value(value);478 }479 Val::Obj(out.build())480 }481 _ => a,482 })483}