git.delta.rocks / jrsonnet / refs/commits / 44f6e2c9e550

difftreelog

source

crates/jrsonnet-stdlib/src/arrays.rs10.6 KiBsourcehistory
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		// TODO: Different mapped array impl avoiding allocating unnecessary vals27		|| 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		// Makes sense mapped object should be ordered the same way, should not break anything when the output is not ordered (the default).81		// The thrown error might be different, but jsonnet82		// does not specify the evaluation order.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						// TODO: extend211						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}