difftreelog
fix experimental features build
in: master
3 files changed
cmds/jrsonnet/src/main.rsdiffbeforeafterboth--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -186,8 +186,12 @@
};
let tla = opts.tla.tla_opts()?;
- #[allow(unused_mut)]
- let mut val = apply_tla(s, &tla, val)?;
+ #[allow(
+ // It is not redundant/unused in exp-apply
+ unused_mut,
+ clippy::redundant_clone,
+ )]
+ let mut val = apply_tla(s.clone(), &tla, val)?;
#[cfg(feature = "exp-apply")]
for apply in opts.input.exp_apply {
crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth1#![allow(non_snake_case)]23use jrsonnet_evaluator::{4 bail,5 function::{builtin, FuncVal},6 runtime_error,7 typed::{BoundedI32, BoundedUsize, Either2, NativeFn, Typed},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 || Ok(ArrValue::range_exclusive(0, *sz).map(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<i32>,52 end: Option<i32>,53 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,54) -> Result<Val> {55 indexable.slice(index, end, step).map(Val::from)56}5758#[builtin]59pub fn builtin_map(func: FuncVal, arr: IndexableVal) -> ArrValue {60 let arr = arr.to_array();61 arr.map(func)62}6364#[builtin]65pub fn builtin_map_with_index(func: FuncVal, arr: IndexableVal) -> ArrValue {66 let arr = arr.to_array();67 arr.map_with_index(func)68}6970#[builtin]71pub fn builtin_map_with_key(func: FuncVal, obj: ObjValue) -> Result<ObjValue> {72 let mut out = ObjValueBuilder::new();73 for (k, v) in obj.iter() {74 let v = v?;75 out.field(k.clone())76 .value(func.evaluate_simple(&(k, v), false)?);77 }78 Ok(out.build())79}8081#[builtin]82pub fn builtin_flatmap(83 func: NativeFn<((Either![String, Val],), Val)>,84 arr: IndexableVal,85) -> Result<IndexableVal> {86 use std::fmt::Write;87 match arr {88 IndexableVal::Str(str) => {89 let mut out = String::new();90 for c in str.chars() {91 match func(Either2::A(c.to_string()))? {92 Val::Str(o) => write!(out, "{o}").unwrap(),93 Val::Null => continue,94 _ => bail!("in std.join all items should be strings"),95 };96 }97 Ok(IndexableVal::Str(out.into()))98 }99 IndexableVal::Arr(a) => {100 let mut out = Vec::new();101 for el in a.iter() {102 let el = el?;103 match func(Either2::B(el))? {104 Val::Arr(o) => {105 for oe in o.iter() {106 out.push(oe?);107 }108 }109 Val::Null => continue,110 _ => bail!("in std.join all items should be arrays"),111 };112 }113 Ok(IndexableVal::Arr(out.into()))114 }115 }116}117118#[builtin]119pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {120 arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(val.clone(),), false)?))121}122123#[builtin]124pub fn builtin_filter_map(125 filter_func: FuncVal,126 map_func: FuncVal,127 arr: ArrValue,128) -> Result<ArrValue> {129 Ok(builtin_filter(filter_func, arr)?.map(map_func))130}131132#[builtin]133pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {134 let mut acc = init;135 for i in arr.iter() {136 acc = func.evaluate_simple(&(acc, i?), false)?;137 }138 Ok(acc)139}140141#[builtin]142pub fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {143 let mut acc = init;144 for i in arr.iter().rev() {145 acc = func.evaluate_simple(&(i?, acc), false)?;146 }147 Ok(acc)148}149150#[builtin]151pub fn builtin_range(from: i32, to: i32) -> Result<ArrValue> {152 if to < from {153 return Ok(ArrValue::empty());154 }155 Ok(ArrValue::range_inclusive(from, to))156}157158#[builtin]159pub fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {160 use std::fmt::Write;161 Ok(match sep {162 IndexableVal::Arr(joiner_items) => {163 let mut out = Vec::new();164165 let mut first = true;166 for item in arr.iter() {167 let item = item?.clone();168 if let Val::Arr(items) = item {169 if !first {170 out.reserve(joiner_items.len());171 // TODO: extend172 for item in joiner_items.iter() {173 out.push(item?);174 }175 }176 first = false;177 out.reserve(items.len());178 for item in items.iter() {179 out.push(item?);180 }181 } else if matches!(item, Val::Null) {182 continue;183 } else {184 bail!("in std.join all items should be arrays");185 }186 }187188 IndexableVal::Arr(out.into())189 }190 IndexableVal::Str(sep) => {191 let mut out = String::new();192193 let mut first = true;194 for item in arr.iter() {195 let item = item?.clone();196 if let Val::Str(item) = item {197 if !first {198 out += &sep;199 }200 first = false;201 write!(out, "{item}").unwrap();202 } else if matches!(item, Val::Null) {203 continue;204 } else {205 bail!("in std.join all items should be strings");206 }207 }208209 IndexableVal::Str(out.into())210 }211 })212}213214#[builtin]215pub fn builtin_lines(arr: ArrValue) -> Result<IndexableVal> {216 builtin_join(217 IndexableVal::Str("\n".into()),218 ArrValue::extended(arr, ArrValue::eager(vec![Val::string("")])),219 )220}221222#[builtin]223pub fn builtin_resolve_path(f: String, r: String) -> String {224 let Some(pos) = f.rfind('/') else {225 return r;226 };227 format!("{}{}", &f[..=pos], r)228}229230pub fn deep_join_inner(out: &mut String, arr: IndexableVal) -> Result<()> {231 use std::fmt::Write;232 match arr {233 IndexableVal::Str(s) => write!(out, "{s}").expect("no error"),234 IndexableVal::Arr(arr) => {235 for ele in arr.iter() {236 let indexable = IndexableVal::from_untyped(ele?)?;237 deep_join_inner(out, indexable)?;238 }239 }240 }241 Ok(())242}243244#[builtin]245pub fn builtin_deep_join(arr: IndexableVal) -> Result<String> {246 let mut out = String::new();247 deep_join_inner(&mut out, arr)?;248 Ok(out)249}250251#[builtin]252pub fn builtin_reverse(arr: ArrValue) -> ArrValue {253 arr.reversed()254}255256#[builtin]257pub fn builtin_any(arr: ArrValue) -> Result<bool> {258 for v in arr.iter() {259 let v = bool::from_untyped(v?)?;260 if v {261 return Ok(true);262 }263 }264 Ok(false)265}266267#[builtin]268pub fn builtin_all(arr: ArrValue) -> Result<bool> {269 for v in arr.iter() {270 let v = bool::from_untyped(v?)?;271 if !v {272 return Ok(false);273 }274 }275 Ok(true)276}277278#[builtin]279pub fn builtin_member(arr: IndexableVal, x: Val) -> Result<bool> {280 match arr {281 IndexableVal::Str(str) => {282 let x: IStr = IStr::from_untyped(x)?;283 Ok(!x.is_empty() && str.contains(&*x))284 }285 IndexableVal::Arr(a) => {286 for item in a.iter() {287 let item = item?;288 if equals(&item, &x)? {289 return Ok(true);290 }291 }292 Ok(false)293 }294 }295}296297#[builtin]298pub fn builtin_find(value: Val, arr: ArrValue) -> Result<Vec<usize>> {299 let mut out = Vec::new();300 for (i, ele) in arr.iter().enumerate() {301 let ele = ele?;302 if equals(&ele, &value)? {303 out.push(i);304 }305 }306 Ok(out)307}308309#[builtin]310pub fn builtin_contains(arr: IndexableVal, elem: Val) -> Result<bool> {311 builtin_member(arr, elem)312}313314#[builtin]315pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {316 let mut count = 0;317 for item in arr.iter() {318 if equals(&item?, &x)? {319 count += 1;320 }321 }322 Ok(count)323}324325#[builtin]326pub fn builtin_avg(arr: Vec<f64>, onEmpty: Option<Thunk<Val>>) -> Result<Val> {327 if arr.is_empty() {328 return eval_on_empty(onEmpty);329 }330 Ok(Val::try_num(arr.iter().sum::<f64>() / (arr.len() as f64))?)331}332333#[builtin]334pub fn builtin_remove_at(arr: ArrValue, at: i32) -> Result<ArrValue> {335 let newArrLeft = arr.clone().slice(None, Some(at), None);336 let newArrRight = arr.slice(Some(at + 1), None, None);337338 Ok(ArrValue::extended(newArrLeft, newArrRight))339}340341#[builtin]342pub fn builtin_remove(arr: ArrValue, elem: Val) -> Result<ArrValue> {343 for (index, item) in arr.iter().enumerate() {344 if equals(&item?, &elem)? {345 return builtin_remove_at(arr.clone(), index as i32);346 }347 }348 Ok(arr)349}350351#[builtin]352pub fn builtin_flatten_arrays(arrs: Vec<ArrValue>) -> ArrValue {353 pub fn flatten_inner(values: &[ArrValue]) -> ArrValue {354 if values.len() == 1 {355 return values[0].clone();356 } else if values.len() == 2 {357 return ArrValue::extended(values[0].clone(), values[1].clone());358 }359 let (a, b) = values.split_at(values.len() / 2);360 ArrValue::extended(flatten_inner(a), flatten_inner(b))361 }362 if arrs.is_empty() {363 return ArrValue::empty();364 } else if arrs.len() == 1 {365 return arrs.into_iter().next().expect("single");366 }367 flatten_inner(&arrs)368}369370#[builtin]371pub fn builtin_flatten_deep_array(value: Val) -> Result<Vec<Val>> {372 fn process(value: Val, out: &mut Vec<Val>) -> Result<()> {373 match value {374 Val::Arr(arr) => {375 for ele in arr.iter() {376 process(ele?, out)?;377 }378 }379 _ => out.push(value),380 }381 Ok(())382 }383 let mut out = Vec::new();384 process(value, &mut out)?;385 Ok(out)386}387388#[builtin]389pub fn builtin_prune(390 a: Val,391392 #[default(false)]393 #[cfg(feature = "exp-preserve-order")]394 preserve_order: bool,395) -> Result<Val> {396 fn is_content(val: &Val) -> bool {397 match val {398 Val::Null => false,399 Val::Arr(a) => !a.is_empty(),400 Val::Obj(o) => !o.is_empty(),401 _ => true,402 }403 }404 Ok(match a {405 Val::Arr(a) => {406 let mut out = Vec::new();407 for (i, ele) in a.iter().enumerate() {408 let ele = ele409 .and_then(|v| {410 builtin_prune(411 v,412 #[cfg(feature = "exp-preserve-order")]413 preserve_order,414 )415 })416 .with_description(|| format!("elem <{i}> pruning"))?;417 if is_content(&ele) {418 out.push(ele);419 }420 }421 Val::Arr(ArrValue::eager(out))422 }423 Val::Obj(o) => {424 let mut out = ObjValueBuilder::new();425 for (name, value) in o.iter(426 #[cfg(feature = "exp-preserve-order")]427 preserve_order,428 ) {429 let value = value430 .and_then(|v| {431 builtin_prune(432 v,433 #[cfg(feature = "exp-preserve-order")]434 preserve_order,435 )436 })437 .with_description(|| format!("field <{name}> pruning"))?;438 if !is_content(&value) {439 continue;440 }441 out.field(name).value(value);442 }443 Val::Obj(out.build())444 }445 _ => a,446 })447}crates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/misc.rs
+++ b/crates/jrsonnet-stdlib/src/misc.rs
@@ -147,7 +147,14 @@
if equals(&a, &b)? {
return Ok(true);
}
- let format = JsonFormat::std_to_json(" ".to_owned(), "\n", ": ");
+ // TODO: Use debug output format
+ let format = JsonFormat::std_to_json(
+ " ".to_owned(),
+ "\n",
+ ": ",
+ #[cfg(feature = "exp-preserve-order")]
+ true,
+ );
let a = a.manifest(&format).description("<a> manifestification")?;
let b = b.manifest(&format).description("<b> manifestification")?;
bail!("assertion failed: A != B\nA: {a}\nB: {b}")
@@ -161,8 +168,30 @@
let Some(target) = target.as_obj() else {
return Ok(Val::Obj(patch));
};
- let target_fields = target.fields().into_iter().collect::<BTreeSet<IStr>>();
- let patch_fields = patch.fields().into_iter().collect::<BTreeSet<IStr>>();
+ let target_fields = target
+ .fields(
+ // FIXME: Makes no sense to preserve order for BTreeSet, it would be better to use IndexSet here?
+ // But IndexSet won't allow fast ordered union...
+ // // Makes sense to preserve source ordering where possible.
+ // // May affect evaluation order, but it is not specified by jsonnet spec.
+ // #[cfg(feature = "exp-preserve-order")]
+ // true,
+ #[cfg(feature = "exp-preserve-order")]
+ false,
+ )
+ .into_iter()
+ .collect::<BTreeSet<IStr>>();
+ let patch_fields = patch
+ .fields(
+ // No need to look at the patch field order, I think?
+ // New fields (that will be appended at the end) will be alphabeticaly-ordered,
+ // but it is fine for jsonpatch, I don't think people write jsonpatch in jsonnet,
+ // when they can use mixins.
+ #[cfg(feature = "exp-preserve-order")]
+ false,
+ )
+ .into_iter()
+ .collect::<BTreeSet<IStr>>();
let mut out = ObjValueBuilder::new();
for field in target_fields.union(&patch_fields) {