--- a/cmds/jrsonnet-fmt/src/main.rs +++ b/cmds/jrsonnet-fmt/src/main.rs @@ -356,16 +356,16 @@ impl Printable for Member { fn print(&self, out: &mut PrintItems) { match self { - Member::MemberBindStmt(b) => { + Self::MemberBindStmt(b) => { p!(out, { b.obj_local() }) } - Member::MemberAssertStmt(ass) => { + Self::MemberAssertStmt(ass) => { p!(out, { ass.assertion() }) } - Member::MemberFieldNormal(n) => { + Self::MemberFieldNormal(n) => { p!(out, {n.field_name()} if(n.plus_token().is_some())({n.plus_token()}) {n.visibility()} str(" ") {n.expr()}) } - Member::MemberFieldMethod(m) => { + Self::MemberFieldMethod(m) => { p!(out, {m.field_name()} {m.params_desc()} {m.visibility()} str(" ") {m.expr()}) } } --- a/crates/jrsonnet-evaluator/src/manifest.rs +++ b/crates/jrsonnet-evaluator/src/manifest.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, fmt::Write}; -use crate::{bail, Result, State, Val}; +use crate::{bail, Result, ResultExt, State, Val}; pub trait ManifestFormat { fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()>; @@ -235,7 +235,8 @@ } } buf.push_str(cur_padding); - manifest_json_ex_buf(&item?, buf, cur_padding, options)?; + manifest_json_ex_buf(&item?, buf, cur_padding, options) + .with_description(|| format!("elem <{i}> manifestification"))?; } cur_padding.truncate(old_len); --- a/crates/jrsonnet-evaluator/src/obj.rs +++ b/crates/jrsonnet-evaluator/src/obj.rs @@ -278,7 +278,7 @@ } impl ObjectLike for ThisOverride { fn with_this(&self, _me: ObjValue, this: ObjValue) -> ObjValue { - ObjValue::new(ThisOverride { + ObjValue::new(Self { inner: self.inner.clone(), this, }) @@ -398,7 +398,7 @@ self.get_for(key, self.0.this().unwrap_or_else(|| self.clone())) } - pub fn get_for(&self, key: IStr, this: ObjValue) -> Result> { + pub fn get_for(&self, key: IStr, this: Self) -> Result> { self.0.get_for(key, this) } @@ -410,7 +410,7 @@ Ok(value) } - fn get_raw(&self, key: IStr, this: ObjValue) -> Result> { + fn get_raw(&self, key: IStr, this: Self) -> Result> { self.0.get_for_uncached(key, this) } @@ -422,7 +422,7 @@ // FIXME: Should it use `self.0.this()` in case of standalone super? self.run_assertions_raw(self.clone()) } - fn run_assertions_raw(&self, this: ObjValue) -> Result<()> { + fn run_assertions_raw(&self, this: Self) -> Result<()> { self.0.run_assertions_raw(this) } --- a/crates/jrsonnet-stdlib/src/arrays.rs +++ b/crates/jrsonnet-stdlib/src/arrays.rs @@ -6,7 +6,7 @@ runtime_error, typed::{BoundedI32, BoundedUsize, Either2, NativeFn, Typed}, val::{equals, ArrValue, IndexableVal}, - Either, IStr, Result, Thunk, Val, + Either, IStr, ObjValueBuilder, Result, ResultExt, Thunk, Val, }; pub(crate) fn eval_on_empty(on_empty: Option>) -> Result { @@ -21,16 +21,17 @@ pub fn builtin_make_array(sz: BoundedI32<0, { i32::MAX }>, func: FuncVal) -> Result { if *sz == 0 { return Ok(ArrValue::empty()); - } - if let Some(trivial) = func.evaluate_trivial() { - let mut out = Vec::with_capacity(*sz as usize); - for _ in 0..*sz { - out.push(trivial.clone()) - } - Ok(ArrValue::eager(out)) - } else { - Ok(ArrValue::range_exclusive(0, *sz).map(func)) } + func.evaluate_trivial().map_or_else( + || Ok(ArrValue::range_exclusive(0, *sz).map(func)), + |trivial| { + let mut out = Vec::with_capacity(*sz as usize); + for _ in 0..*sz { + out.push(trivial.clone()); + } + Ok(ArrValue::eager(out)) + }, + ) } #[builtin] @@ -180,7 +181,7 @@ out += &sep; } first = false; - write!(out, "{item}").unwrap() + write!(out, "{item}").unwrap(); } else if matches!(item, Val::Null) { continue; } else { @@ -320,3 +321,61 @@ process(value, &mut out)?; Ok(out) } + +#[builtin] +pub fn builtin_prune( + a: Val, + #[cfg(feature = "exp-preserve-order")] preserve_order: bool, +) -> Result { + fn is_content(val: &Val) -> bool { + match val { + Val::Null => false, + Val::Arr(a) => !a.is_empty(), + Val::Obj(o) => !o.is_empty(), + _ => true, + } + } + Ok(match a { + Val::Arr(a) => { + let mut out = Vec::new(); + for (i, ele) in a.iter().enumerate() { + let ele = ele + .and_then(|v| { + builtin_prune( + v, + #[cfg(feature = "exp-preserve-order")] + preserve_order, + ) + }) + .with_description(|| format!("elem <{i}> pruning"))?; + if is_content(&ele) { + out.push(ele); + } + } + Val::Arr(ArrValue::eager(out)) + } + Val::Obj(o) => { + let mut out = ObjValueBuilder::new(); + for (name, value) in o.iter( + #[cfg(feature = "exp-preserve-order")] + preserve_order, + ) { + let value = value + .and_then(|v| { + builtin_prune( + v, + #[cfg(feature = "exp-preserve-order")] + preserve_order, + ) + }) + .with_description(|| format!("field <{name}> pruning"))?; + if !is_content(&value) { + continue; + } + out.field(name).value(value); + } + Val::Obj(out.build()) + } + _ => a, + }) +} --- a/crates/jrsonnet-stdlib/src/lib.rs +++ b/crates/jrsonnet-stdlib/src/lib.rs @@ -92,6 +92,7 @@ ("remove", builtin_remove::INST), ("flattenArrays", builtin_flatten_arrays::INST), ("flattenDeepArray", builtin_flatten_deep_array::INST), + ("prune", builtin_prune::INST), ("filterMap", builtin_filter_map::INST), // Math ("abs", builtin_abs::INST), --- a/crates/jrsonnet-stdlib/src/std.jsonnet +++ b/crates/jrsonnet-stdlib/src/std.jsonnet @@ -209,25 +209,6 @@ local arr = std.split(f, '/'); std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]), - prune(a):: - local isContent(b) = - if b == null then - false - else if std.isArray(b) then - std.length(b) > 0 - else if std.isObject(b) then - std.length(b) > 0 - else - true; - if std.isArray(a) then - [std.prune(x) for x in a if isContent($.prune(x))] - else if std.isObject(a) then { - [x]: $.prune(a[x]) - for x in std.objectFields(a) - if isContent(std.prune(a[x])) - } else - a, - find(value, arr):: if !std.isArray(arr) then error 'find second parameter should be an array, got ' + std.type(arr)