git.delta.rocks / jrsonnet / refs/commits / 2fdeec2fa073

difftreelog

source

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