--- a/crates/jrsonnet-stdlib/src/lib.rs +++ b/crates/jrsonnet-stdlib/src/lib.rs @@ -175,6 +175,7 @@ // Sets ("setMember", builtin_set_member::INST), ("setInter", builtin_set_inter::INST), + ("setDiff", builtin_set_diff::INST), // Compat ("__compare", builtin___compare::INST), ] --- a/crates/jrsonnet-stdlib/src/sets.rs +++ b/crates/jrsonnet-stdlib/src/sets.rs @@ -71,3 +71,48 @@ } Ok(ArrValue::lazy(out)) } +#[builtin] +#[allow(non_snake_case, clippy::redundant_closure)] +pub fn builtin_set_diff(a: ArrValue, b: ArrValue, keyF: Option) -> Result { + let mut a = a.iter_lazy(); + let mut b = b.iter_lazy(); + + let keyF = keyF + .unwrap_or(FuncVal::identity()) + .into_native::<((Thunk,), Val)>(); + let keyF = |v| keyF(v); + + let mut av = a.next(); + let mut bv = b.next(); + let mut ak = av.clone().map(keyF).transpose()?; + let mut bk = bv.map(keyF).transpose()?; + + let mut out = Vec::new(); + while let (Some(ac), Some(bc)) = (&ak, &bk) { + match evaluate_compare_op(ac, bc, BinaryOpType::Lt)? { + Ordering::Less => { + // In a, but not in b + out.push(av.clone().expect("ak != None")); + av = a.next(); + ak = av.clone().map(keyF).transpose()?; + } + Ordering::Greater => { + bv = b.next(); + bk = bv.map(keyF).transpose()?; + } + Ordering::Equal => { + av = a.next(); + ak = av.clone().map(keyF).transpose()?; + bv = b.next(); + bk = bv.map(keyF).transpose()?; + } + }; + } + while let Some(ac) = &ak { + // In a, but not in b + out.push(av.clone().expect("ak != None")); + av = a.next(); + ak = av.clone().map(keyF).transpose()?; + } + Ok(ArrValue::lazy(out)) +} --- a/crates/jrsonnet-stdlib/src/std.jsonnet +++ b/crates/jrsonnet-stdlib/src/std.jsonnet @@ -214,21 +214,6 @@ aux(a, b, i, j + 1, acc + [b[j]]) tailstrict; aux(a, b, 0, 0, []), - setDiff(a, b, keyF=id):: - local aux(a, b, i, j, acc) = - if i >= std.length(a) then - acc - else if j >= std.length(b) then - acc + a[i:] - else - if keyF(a[i]) == keyF(b[j]) then - aux(a, b, i + 1, j + 1, acc) tailstrict - else if keyF(a[i]) < keyF(b[j]) then - aux(a, b, i + 1, j, acc + [a[i]]) tailstrict - else - aux(a, b, i, j + 1, acc) tailstrict; - aux(a, b, 0, 0, []) tailstrict, - mergePatch(target, patch):: if std.isObject(patch) then local target_object =