--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs +++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs @@ -128,6 +128,8 @@ ("parseJson".into(), builtin_parse_json), ("asciiUpper".into(), builtin_ascii_upper), ("asciiLower".into(), builtin_ascii_lower), + ("member".into(), builtin_member), + ("count".into(), builtin_count), ].iter().cloned().collect() }; } @@ -854,6 +856,46 @@ }) } +fn builtin_member(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result { + parse_args!(context, "member", args, 2, [ + 0, arr: ty!((array | string)); + 1, x: ty!(any); + ], { + match arr { + Val::Str(s) => { + let x = x.try_cast_str("x should be string")?; + Ok(Val::Bool(!x.is_empty() && s.contains(&*x))) + } + Val::Arr(a) => { + for item in a.iter() { + let item = item?; + if equals(&item, &x)? { + return Ok(Val::Bool(true)); + } + } + Ok(Val::Bool(false)) + } + _ => unreachable!(), + } + }) +} + +fn builtin_count(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result { + parse_args!(context, "count", args, 2, [ + 0, arr: ty!(array) => Val::Arr; + 1, x: ty!(any); + ], { + let mut count = 0; + for item in arr.iter() { + let item = item?; + if equals(&item, &x)? { + count += 1; + } + } + Ok(Val::Num(count as f64)) + }) +} + pub fn call_builtin( context: Context, loc: Option<&ExprLocation>, --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -1051,4 +1051,21 @@ assert_eval!(r#"std.assertEqual(std.asciiUpper("aBc😀"), "ABC😀")"#); assert_eval!(r#"std.assertEqual(std.asciiLower("aBc😀"), "abc😀")"#); } + + #[test] + fn test_member() { + assert_eval!(r#"!std.member("", "")"#); + assert_eval!(r#"std.member("abc", "a")"#); + assert_eval!(r#"!std.member("abc", "d")"#); + assert_eval!(r#"!std.member([], "")"#); + assert_eval!(r#"std.member(["a", "b", "c"], "a")"#); + assert_eval!(r#"!std.member(["a", "b", "c"], "d")"#); + } + + #[test] + fn test_count() { + assert_eval!(r#"std.assertEqual(std.count([], ""), 0)"#); + assert_eval!(r#"std.assertEqual(std.count(["a", "b", "a"], "d"), 0)"#); + assert_eval!(r#"std.assertEqual(std.count(["a", "b", "a"], "a"), 2)"#); + } } --- a/crates/jrsonnet-stdlib/src/std.jsonnet +++ b/crates/jrsonnet-stdlib/src/std.jsonnet @@ -143,14 +143,9 @@ slice:: $intrinsic(slice), - member(arr, x):: - if std.isArray(arr) then - std.count(arr, x) > 0 - else if std.isString(arr) then - std.length(std.findSubstr(x, arr)) > 0 - else error 'std.member first argument must be an array or a string', + member:: $intrinsic(member), - count(arr, x):: std.length(std.filter(function(v) v == x, arr)), + count:: $intrinsic(count), mod:: $intrinsic(mod),