mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
More OPA conformant semantics (#62)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
8a73b4bef9
commit
73ee18f002
42
README.md
42
README.md
@@ -15,32 +15,33 @@ Regorus is available as a library that can be easily integrated into your Rust p
|
||||
|
||||
## Getting Started
|
||||
|
||||
[regorus](examples/regorus.rs) is an example program that shows how to integrate Regorus into your project and evaluate Rego policies.
|
||||
[examples/regorus](examples/regorus.rs) is an example program that shows how to integrate Regorus into your project and evaluate Rego policies.
|
||||
|
||||
To build it, do
|
||||
To build and install it, do
|
||||
|
||||
cargo build -r --example regorus
|
||||
cargo install --example regorus --path .
|
||||
|
||||
|
||||
Check that the regorus example program is working
|
||||
|
||||
$ target/release/examples/regorus
|
||||
$ regorus
|
||||
Usage: regorus <COMMAND>
|
||||
|
||||
Commands:
|
||||
eval Evaluate a Rego Query
|
||||
lex Tokenize a Rego policy
|
||||
parse Parse q Rego policy
|
||||
parse Parse a Rego policy
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
-V, --version Print versionUsage: regorus <COMMAND>
|
||||
|
||||
|
||||
|
||||
First, let's evaluate a simple Rego expression `1*2+3`
|
||||
|
||||
target/release/examples/regorus eval "1*2+3"
|
||||
regorus eval "1*2+3"
|
||||
|
||||
This produces the following output
|
||||
|
||||
@@ -63,16 +64,11 @@ This produces the following output
|
||||
|
||||
Next, evaluate a sample [policy](examples/example.rego) and [input](examples/input.json) (borrowed from [Rego tutorial](https://www.openpolicyagent.org/docs/latest/#2-try-opa-eval)):
|
||||
|
||||
target/release/examples/regorus eval -d examples/example.rego -i examples/input.json data.example
|
||||
regorus eval -d examples/example.rego -i examples/input.json data.example
|
||||
|
||||
Finally, evaluate real-world [policies](tests/aci/) used in Azure Container Instances (ACI)
|
||||
|
||||
target/release/examples/regorus eval -d tests/aci/framework.rego \
|
||||
-d tests/aci/policy.rego \
|
||||
-d tests/aci/api.rego \
|
||||
-d tests/aci/data.json \
|
||||
-i tests/aci/input.json \
|
||||
data.policy.mount_overlay=x
|
||||
regorus eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.policy.mount_overlay=x
|
||||
|
||||
|
||||
## ACI Policies
|
||||
@@ -94,26 +90,24 @@ Regorus successfully passes the ACI policy test-suite. It is fast and can run ea
|
||||
|
||||
Run the ACI policies in the `tests/aci` directory, using data `tests/aci/data.json` and input `tests/aci/input.json`:
|
||||
|
||||
target/release/examples/regorus eval \
|
||||
-b tests/aci \
|
||||
-d tests/aci/data.json \
|
||||
-i tests/aci/input.json \
|
||||
data.framework.mount_overlay=x
|
||||
regorus eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.policy.mount_overlay=x
|
||||
|
||||
|
||||
Verify that [OPA](https://github.com/open-policy-agent/opa/releases) produces the same output
|
||||
|
||||
diff <(target/release/examples/regorus eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.framework.mount_overlay=x) <(opa eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.framework.mount_overlay=x)
|
||||
diff <(regorus eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.framework.mount_overlay=x) \
|
||||
<(opa eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.framework.mount_overlay=x)
|
||||
|
||||
## Performance
|
||||
|
||||
To check how fast Regorus runs on your system, first install a tool like [hyperfine](https://github.com/sharkdp/hyperfine).
|
||||
|
||||
cargo install hyperfine
|
||||
cargo install hyperfine
|
||||
|
||||
Then benchmark evaluation of the ACI policies,
|
||||
|
||||
$ hyperfine "target/release/examples/regorus eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.framework.mount_overlay=x"
|
||||
Benchmark 1: target/release/examples/regorus eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.framework.mount_overlay=x
|
||||
$ hyperfine "regorus eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.framework.mount_overlay=x"
|
||||
Benchmark 1: regorus eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.framework.mount_overlay=x
|
||||
Time (mean ± σ): 4.6 ms ± 0.2 ms [User: 4.1 ms, System: 0.4 ms]
|
||||
Range (min … max): 4.4 ms … 6.0 ms 422 runs
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ enum RegorusCommand {
|
||||
verbose: bool,
|
||||
},
|
||||
|
||||
/// Parse q Rego policy.
|
||||
/// Parse a Rego policy.
|
||||
Parse {
|
||||
/// Rego policy file.
|
||||
file: String,
|
||||
|
||||
@@ -321,6 +321,7 @@ pub enum Rule {
|
||||
Default {
|
||||
span: Span,
|
||||
refr: Ref<Expr>,
|
||||
args: Vec<Ref<Expr>>,
|
||||
op: AssignOp,
|
||||
value: Ref<Expr>,
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("sum", (sum, 1));
|
||||
}
|
||||
|
||||
fn count(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn count(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "count", params, args, 1)?;
|
||||
|
||||
Ok(Value::from(Number::from(match &args[0] {
|
||||
@@ -38,7 +38,7 @@ fn count(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
})))
|
||||
}
|
||||
|
||||
fn max(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn max(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "max", params, args, 1)?;
|
||||
|
||||
Ok(match &args[0] {
|
||||
@@ -53,7 +53,7 @@ fn max(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
fn min(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn min(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "min", params, args, 1)?;
|
||||
|
||||
Ok(match &args[0] {
|
||||
@@ -68,7 +68,7 @@ fn min(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
fn product(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn product(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "product", params, args, 1)?;
|
||||
|
||||
let mut v = Number::from(1_u64);
|
||||
@@ -93,7 +93,7 @@ fn product(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
}))
|
||||
}
|
||||
|
||||
fn sort(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn sort(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "sort", params, args, 1)?;
|
||||
Ok(match &args[0] {
|
||||
Value::Array(a) => {
|
||||
@@ -110,7 +110,7 @@ fn sort(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
fn sum(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn sum(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "sum", params, args, 1)?;
|
||||
|
||||
let mut v = Number::from(0_u64);
|
||||
|
||||
@@ -18,7 +18,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("array.slice", (slice, 3));
|
||||
}
|
||||
|
||||
fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "array.concat";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let mut v1 = ensure_array(name, ¶ms[0], args[0].clone())?;
|
||||
@@ -28,7 +28,7 @@ fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Ok(Value::Array(v1))
|
||||
}
|
||||
|
||||
fn reverse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn reverse(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "array.reverse";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
@@ -37,7 +37,7 @@ fn reverse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Ok(Value::Array(v1))
|
||||
}
|
||||
|
||||
fn slice(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn slice(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "array.slice";
|
||||
ensure_args_count(span, name, params, args, 3)?;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("bits.xor", (xor, 2));
|
||||
}
|
||||
|
||||
fn and(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn and(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "bits.and";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -34,7 +34,7 @@ fn and(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
fn lsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn lsh(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "bits.lsh";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -47,7 +47,7 @@ fn lsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
fn negate(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn negate(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "bits.negate";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
@@ -59,7 +59,7 @@ fn negate(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
fn or(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn or(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "bits.or";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -72,7 +72,7 @@ fn or(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
fn rsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn rsh(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "bits.rsh";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -85,7 +85,7 @@ fn rsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
fn xor(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn xor(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "bits.xor";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
|
||||
@@ -15,12 +15,13 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("to_number", (to_number, 1));
|
||||
}
|
||||
|
||||
fn to_number(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn to_number(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "to_number";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
let span = params[0].span();
|
||||
Ok(match &args[0] {
|
||||
Value::Null => Value::from(0u64),
|
||||
Value::Bool(true) => Value::from(1u64),
|
||||
Value::Bool(false) => Value::from(0u64),
|
||||
Value::Number(_) => args[0].clone(),
|
||||
@@ -34,7 +35,9 @@ fn to_number(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
bail!(span.error(format!("`{name}` expects bool/number/string argument.").as_str()));
|
||||
bail!(
|
||||
span.error(format!("`{name}` expects bool/number/string/null argument.").as_str())
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -28,7 +28,12 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("crypto.sha256", (crypto_sha256, 1));
|
||||
}
|
||||
|
||||
fn hmac_equal_fixed_time(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn hmac_equal_fixed_time(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "crypto.hmac.equal";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -41,7 +46,7 @@ fn hmac_equal_fixed_time(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> R
|
||||
)))
|
||||
}
|
||||
|
||||
fn hmac_md5(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn hmac_md5(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "crypto.hmac.md5";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -57,7 +62,7 @@ fn hmac_md5(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
Ok(Value::String(hex::encode(result.into_bytes()).into()))
|
||||
}
|
||||
|
||||
fn hmac_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn hmac_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "crypto.hmac.sha1";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -73,7 +78,7 @@ fn hmac_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
Ok(Value::String(hex::encode(result.into_bytes()).into()))
|
||||
}
|
||||
|
||||
fn hmac_sha256(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn hmac_sha256(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "crypto.hmac.sha256";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -89,7 +94,7 @@ fn hmac_sha256(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
|
||||
Ok(Value::String(hex::encode(result.into_bytes()).into()))
|
||||
}
|
||||
|
||||
fn hmac_sha512(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn hmac_sha512(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "crypto.hmac.sha512";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -105,7 +110,7 @@ fn hmac_sha512(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
|
||||
Ok(Value::String(hex::encode(result.into_bytes()).into()))
|
||||
}
|
||||
|
||||
fn crypto_md5(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn crypto_md5(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "crypto.md5";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
@@ -119,7 +124,7 @@ fn crypto_md5(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
|
||||
Ok(Value::String(hex::encode(result).into()))
|
||||
}
|
||||
|
||||
fn crypto_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn crypto_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "crypto.sha1";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
@@ -133,7 +138,12 @@ fn crypto_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
|
||||
Ok(Value::String(hex::encode(result).into()))
|
||||
}
|
||||
|
||||
fn crypto_sha256(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn crypto_sha256(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "crypto.sha256";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
// Symbol analyzer must ensure that vars used by print are defined before
|
||||
// the print statement. Scheduler must ensure the above constraint.
|
||||
// Additionally interpreter must allow undefined inputs to print.
|
||||
fn print(span: &Span, _params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn print(span: &Span, _params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
if args.len() > MAX_ARGS as usize {
|
||||
bail!(span.error("print supports up to 100 arguments"));
|
||||
}
|
||||
|
||||
@@ -19,7 +19,13 @@ lazy_static! {
|
||||
let mut m : HashMap<&'static str, BuiltinFcn> = HashMap::new();
|
||||
|
||||
m.insert("all", (all, 1));
|
||||
m.insert("any", (any, 1));
|
||||
m.insert("any", (any, 1));
|
||||
m.insert("cast_array", (cast_array, 1));
|
||||
m.insert("cast_boolean", (cast_boolean, 1));
|
||||
m.insert("cast_null", (cast_null, 1));
|
||||
m.insert("cast_object", (cast_object, 1));
|
||||
m.insert("cast_set", (cast_set, 1));
|
||||
m.insert("cast_string", (cast_string, 1));
|
||||
m.insert("set_diff", (set_diff, 2));
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
@@ -28,7 +34,7 @@ lazy_static! {
|
||||
};
|
||||
}
|
||||
|
||||
fn all(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn all(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "all", params, args, 1)?;
|
||||
|
||||
Ok(Value::Bool(match &args[0] {
|
||||
@@ -41,7 +47,7 @@ fn all(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
}))
|
||||
}
|
||||
|
||||
fn any(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn any(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "any", params, args, 1)?;
|
||||
|
||||
Ok(Value::Bool(match &args[0] {
|
||||
@@ -54,10 +60,70 @@ fn any(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
}))
|
||||
}
|
||||
|
||||
fn set_diff(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn set_diff(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "set_diff";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_set(name, ¶ms[0], args[0].clone())?;
|
||||
let s2 = ensure_set(name, ¶ms[1], args[1].clone())?;
|
||||
Ok(Value::from_set(s1.difference(&s2).cloned().collect()))
|
||||
}
|
||||
|
||||
fn cast_array(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "cast_array";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
match &args[0] {
|
||||
Value::Array(_) => Ok(args[0].clone()),
|
||||
_ if strict => bail!(params[0].span().error("array required")),
|
||||
_ => Ok(Value::Undefined),
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_boolean(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "cast_boolean";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
match &args[0] {
|
||||
Value::Bool(_) => Ok(args[0].clone()),
|
||||
_ if strict => bail!(params[0].span().error("boolean required")),
|
||||
_ => Ok(Value::Undefined),
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_null(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "cast_null";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
match &args[0] {
|
||||
Value::Null => Ok(Value::Null),
|
||||
_ if strict => bail!(params[0].span().error("null required")),
|
||||
_ => Ok(Value::Undefined),
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_object(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "cast_object";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
match &args[0] {
|
||||
Value::Object(_) => Ok(args[0].clone()),
|
||||
_ if strict => bail!(params[0].span().error("object required")),
|
||||
_ => Ok(Value::Undefined),
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_set(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "cast_set";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
match &args[0] {
|
||||
Value::Set(_) => Ok(args[0].clone()),
|
||||
_ if strict => bail!(params[0].span().error("set required")),
|
||||
_ => Ok(Value::Undefined),
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_string(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "cast_string";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
match &args[0] {
|
||||
Value::String(_) => Ok(args[0].clone()),
|
||||
_ if strict => bail!(params[0].span().error("string required")),
|
||||
_ => Ok(Value::Undefined),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,12 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn base64_decode(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn base64_decode(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "base64.decode";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
@@ -38,7 +43,12 @@ fn base64_decode(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Va
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
fn yaml_is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn yaml_is_valid(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "yaml.is_valid";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
@@ -47,7 +57,7 @@ fn yaml_is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Va
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
fn yaml_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn yaml_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "yaml.marshal";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
Ok(Value::String(
|
||||
@@ -58,14 +68,24 @@ fn yaml_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Val
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
fn yaml_unmarshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn yaml_unmarshal(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "yaml.unmarshal";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let yaml_str = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
Value::from_yaml_str(&yaml_str).with_context(|| span.error("could not deserialize yaml."))
|
||||
}
|
||||
|
||||
fn json_is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn json_is_valid(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "json.is_valid";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
@@ -73,7 +93,7 @@ fn json_is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Va
|
||||
Ok(Value::Bool(Value::from_json_str(&json_str).is_ok()))
|
||||
}
|
||||
|
||||
fn json_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn json_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "json.marshal";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
Ok(Value::String(
|
||||
@@ -83,7 +103,12 @@ fn json_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Val
|
||||
))
|
||||
}
|
||||
|
||||
fn json_unmarshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn json_unmarshal(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "json.unmarshal";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let json_str = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
|
||||
@@ -55,7 +55,7 @@ fn make_glob<'a>(pattern: &'a str, span: &'a Span) -> Result<Glob<'a>> {
|
||||
Glob::new(pattern).or_else(|_| bail!(span.error("invalid glob")))
|
||||
}
|
||||
|
||||
fn glob_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn glob_match(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "glob.match";
|
||||
ensure_args_count(span, name, params, args, 3)?;
|
||||
|
||||
@@ -101,7 +101,7 @@ fn glob_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
|
||||
Ok(Value::Bool(glob.is_match(&value[..])))
|
||||
}
|
||||
|
||||
fn quote_meta(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn quote_meta(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "glob.quote_meta";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ use std::collections::HashMap;
|
||||
use anyhow::Result;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
pub type BuiltinFcn = (fn(&Span, &[Ref<Expr>], &[Value]) -> Result<Value>, u8);
|
||||
pub type BuiltinFcn = (fn(&Span, &[Ref<Expr>], &[Value], bool) -> Result<Value>, u8);
|
||||
|
||||
#[cfg(feature = "deprecated")]
|
||||
pub use deprecated::DEPRECATED;
|
||||
|
||||
@@ -48,28 +48,28 @@ pub fn arithmetic_operation(
|
||||
}))
|
||||
}
|
||||
|
||||
fn abs(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn abs(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "abs", params, args, 1)?;
|
||||
Ok(Value::from(
|
||||
ensure_numeric("abs", ¶ms[0], &args[0])?.abs(),
|
||||
))
|
||||
}
|
||||
|
||||
fn ceil(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn ceil(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "ceil", params, args, 1)?;
|
||||
Ok(Value::from(
|
||||
ensure_numeric("ceil", ¶ms[0], &args[0])?.ceil(),
|
||||
))
|
||||
}
|
||||
|
||||
fn floor(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn floor(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "floor", params, args, 1)?;
|
||||
Ok(Value::from(
|
||||
ensure_numeric("floor", ¶ms[0], &args[0])?.floor(),
|
||||
))
|
||||
}
|
||||
|
||||
fn range(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn range(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "numbers.range", params, args, 2)?;
|
||||
let v1 = ensure_numeric("numbers.range", ¶ms[0], &args[0].clone())?;
|
||||
let v2 = ensure_numeric("numbers.range", ¶ms[1], &args[1].clone())?;
|
||||
@@ -96,14 +96,14 @@ fn range(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Ok(Value::from_array(values))
|
||||
}
|
||||
|
||||
fn round(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn round(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "round", params, args, 1)?;
|
||||
Ok(Value::from(
|
||||
ensure_numeric("round", ¶ms[0], &args[0])?.round(),
|
||||
))
|
||||
}
|
||||
|
||||
fn intn(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn intn(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let fcn = "rand.intn";
|
||||
ensure_args_count(span, fcn, params, args, 2)?;
|
||||
let _ = ensure_string(fcn, ¶ms[0], &args[0])?;
|
||||
|
||||
@@ -122,7 +122,7 @@ fn merge_filters(
|
||||
Ok(filters)
|
||||
}
|
||||
|
||||
fn json_filter(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn json_filter(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "json.filter";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
ensure_object(name, ¶ms[0], args[0].clone())?;
|
||||
@@ -136,7 +136,7 @@ fn json_filter(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
|
||||
Ok(json_filter_impl(&args[0], &filters))
|
||||
}
|
||||
|
||||
fn filter(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn filter(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "object.filter";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let mut obj = ensure_object(name, ¶ms[0], args[0].clone())?;
|
||||
@@ -154,7 +154,7 @@ fn filter(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Ok(Value::Object(obj))
|
||||
}
|
||||
|
||||
fn get(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn get(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "object.get";
|
||||
ensure_args_count(span, name, params, args, 3)?;
|
||||
let obj = ensure_object(name, ¶ms[0], args[0].clone())?;
|
||||
@@ -179,14 +179,14 @@ fn get(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
fn keys(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn keys(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "object.keys";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let obj = ensure_object(name, ¶ms[0], args[0].clone())?;
|
||||
Ok(Value::from_set(obj.keys().cloned().collect()))
|
||||
}
|
||||
|
||||
fn remove(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn remove(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "object.remove";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let mut obj = ensure_object(name, ¶ms[0], args[0].clone())?;
|
||||
@@ -225,7 +225,7 @@ fn is_subset(sup: &Value, sub: &Value) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn subset(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn subset(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "object.subset";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
|
||||
@@ -26,7 +26,12 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("regex.template_match", (regex_template_match, 4));
|
||||
}
|
||||
|
||||
fn find_all_string_submatch_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn find_all_string_submatch_n(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "regex.find_all_string_submatch_n";
|
||||
ensure_args_count(span, name, params, args, 3)?;
|
||||
|
||||
@@ -68,7 +73,7 @@ fn find_all_string_submatch_n(span: &Span, params: &[Ref<Expr>], args: &[Value])
|
||||
))
|
||||
}
|
||||
|
||||
fn find_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn find_n(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "regex.find_n";
|
||||
ensure_args_count(span, name, params, args, 3)?;
|
||||
|
||||
@@ -98,14 +103,19 @@ fn find_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
))
|
||||
}
|
||||
|
||||
fn is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "regex.is_valid";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
Ok(ensure_string(name, ¶ms[0], &args[0])
|
||||
.map_or(Value::Bool(false), |p| Value::Bool(Regex::new(&p).is_ok())))
|
||||
}
|
||||
|
||||
pub fn regex_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
pub fn regex_match(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "regex.match";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let pattern = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -116,7 +126,12 @@ pub fn regex_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<
|
||||
Ok(Value::Bool(pattern.is_match(&value)))
|
||||
}
|
||||
|
||||
fn regex_replace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn regex_replace(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "regex.replace";
|
||||
ensure_args_count(span, name, params, args, 3)?;
|
||||
|
||||
@@ -135,7 +150,7 @@ fn regex_replace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Va
|
||||
))
|
||||
}
|
||||
|
||||
fn regex_split(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn regex_split(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "regex.split";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let pattern = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -151,7 +166,12 @@ fn regex_split(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
|
||||
))
|
||||
}
|
||||
|
||||
fn regex_template_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn regex_template_match(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "regex.template_match";
|
||||
ensure_args_count(span, name, params, args, 4)?;
|
||||
let template = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
|
||||
@@ -19,7 +19,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("semver.is_valid", (is_valid, 1));
|
||||
}
|
||||
|
||||
fn compare(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn compare(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "semver.compare";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -35,7 +35,7 @@ fn compare(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Ok(Value::from(result as i64))
|
||||
}
|
||||
|
||||
fn is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "semver.is_valid";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let v = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
|
||||
@@ -34,7 +34,12 @@ pub fn difference(expr1: &Expr, expr2: &Expr, v1: Value, v2: Value) -> Result<Va
|
||||
Ok(Value::from_set(s1.difference(&s2).cloned().collect()))
|
||||
}
|
||||
|
||||
fn intersection_of_set_of_sets(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn intersection_of_set_of_sets(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "intersection";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let set = ensure_set(name, ¶ms[0], args[0].clone())?;
|
||||
@@ -61,7 +66,12 @@ fn intersection_of_set_of_sets(span: &Span, params: &[Ref<Expr>], args: &[Value]
|
||||
Ok(Value::from_set(res))
|
||||
}
|
||||
|
||||
fn union_of_set_of_sets(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn union_of_set_of_sets(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "union";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let set = ensure_set(name, ¶ms[0], args[0].clone())?;
|
||||
|
||||
@@ -42,7 +42,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("upper", (upper, 1));
|
||||
}
|
||||
|
||||
fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "concat";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let delimiter = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -50,7 +50,7 @@ fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Ok(Value::String(collection.join(&delimiter).into()))
|
||||
}
|
||||
|
||||
fn contains(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn contains(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "contains";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -58,7 +58,7 @@ fn contains(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
Ok(Value::Bool(s1.contains(s2.as_ref())))
|
||||
}
|
||||
|
||||
fn endswith(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn endswith(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "endswith";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -76,7 +76,7 @@ fn format_number(n: &Number, base: u64) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn format_int(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn format_int(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "format_int";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let mut n = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
@@ -97,7 +97,7 @@ fn format_int(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
|
||||
))
|
||||
}
|
||||
|
||||
fn indexof(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn indexof(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "indexof";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -109,7 +109,7 @@ fn indexof(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn indexof_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn indexof_n(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "indexof_n";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -128,14 +128,14 @@ fn indexof_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
Ok(Value::from_array(positions))
|
||||
}
|
||||
|
||||
fn lower(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn lower(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "lower";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
Ok(Value::String(s.to_lowercase().into()))
|
||||
}
|
||||
|
||||
fn replace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn replace(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "replace";
|
||||
ensure_args_count(span, name, params, args, 3)?;
|
||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -144,7 +144,7 @@ fn replace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Ok(Value::String(s.replace(old.as_ref(), new.as_ref()).into()))
|
||||
}
|
||||
|
||||
fn split(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn split(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "replace";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -157,7 +157,7 @@ fn split(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
))
|
||||
}
|
||||
|
||||
fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "sprintf";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let fmt = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -345,7 +345,12 @@ fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Ok(Value::String(s.into()))
|
||||
}
|
||||
|
||||
fn any_prefix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn any_prefix_match(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "strings.any_prefix_match";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -376,7 +381,12 @@ fn any_prefix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result
|
||||
))
|
||||
}
|
||||
|
||||
fn any_suffix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn any_suffix_match(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "strings.any_suffix_match";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
@@ -407,7 +417,7 @@ fn any_suffix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result
|
||||
))
|
||||
}
|
||||
|
||||
fn startswith(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn startswith(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "startswith";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -415,7 +425,7 @@ fn startswith(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
|
||||
Ok(Value::Bool(s1.starts_with(s2.as_ref())))
|
||||
}
|
||||
|
||||
fn replace_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn replace_n(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "trim";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let obj = ensure_object(name, ¶ms[0], args[0].clone())?;
|
||||
@@ -438,14 +448,14 @@ fn replace_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
Ok(Value::String(s.clone()))
|
||||
}
|
||||
|
||||
fn reverse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn reverse(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "reverse";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
Ok(Value::String(s.chars().rev().collect::<String>().into()))
|
||||
}
|
||||
|
||||
fn substring(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn substring(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "substring";
|
||||
ensure_args_count(span, name, params, args, 3)?;
|
||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -470,7 +480,7 @@ fn substring(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
}
|
||||
}
|
||||
|
||||
fn trim(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn trim(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "trim";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -478,7 +488,7 @@ fn trim(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Ok(Value::String(s1.trim_matches(|c| s2.contains(c)).into()))
|
||||
}
|
||||
|
||||
fn trim_left(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn trim_left(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "trim_left";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -488,7 +498,7 @@ fn trim_left(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
))
|
||||
}
|
||||
|
||||
fn trim_prefix(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn trim_prefix(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "trim_prefix";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -499,7 +509,7 @@ fn trim_prefix(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
|
||||
}))
|
||||
}
|
||||
|
||||
fn trim_right(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn trim_right(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "trim_right";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -509,14 +519,14 @@ fn trim_right(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
|
||||
))
|
||||
}
|
||||
|
||||
fn trim_space(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn trim_space(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "trim_space";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
Ok(Value::String(s.trim().into()))
|
||||
}
|
||||
|
||||
fn trim_suffix(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn trim_suffix(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "trim_suffix";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -527,7 +537,7 @@ fn trim_suffix(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
|
||||
}))
|
||||
}
|
||||
|
||||
fn upper(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn upper(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "upper";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
|
||||
@@ -16,7 +16,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("time.now_ns", (now_ns, 0));
|
||||
}
|
||||
|
||||
fn now_ns(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn now_ns(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "time.now_ns";
|
||||
ensure_args_count(span, name, params, args, 0)?;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
|
||||
// Symbol analyzer must ensure that vars used by trace are defined before
|
||||
// the trace statement. Scheduler must ensure the above constraint.
|
||||
fn trace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn trace(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "trace";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let msg = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
|
||||
@@ -22,37 +22,37 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("type_name", (type_name, 1));
|
||||
}
|
||||
|
||||
fn is_array(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn is_array(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "is_array", params, args, 1)?;
|
||||
Ok(Value::Bool(matches!(&args[0], Value::Array(_))))
|
||||
}
|
||||
|
||||
fn is_boolean(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn is_boolean(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "is_boolean", params, args, 1)?;
|
||||
Ok(Value::Bool(matches!(&args[0], Value::Bool(_))))
|
||||
}
|
||||
|
||||
fn is_null(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn is_null(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "is_null", params, args, 1)?;
|
||||
Ok(Value::Bool(matches!(&args[0], Value::Null)))
|
||||
}
|
||||
|
||||
fn is_number(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn is_number(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "is_number", params, args, 1)?;
|
||||
Ok(Value::Bool(matches!(&args[0], Value::Number(_))))
|
||||
}
|
||||
|
||||
fn is_object(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn is_object(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "is_object", params, args, 1)?;
|
||||
Ok(Value::Bool(matches!(&args[0], Value::Object(_))))
|
||||
}
|
||||
|
||||
fn is_set(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn is_set(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "is_set", params, args, 1)?;
|
||||
Ok(Value::Bool(matches!(&args[0], Value::Set(_))))
|
||||
}
|
||||
|
||||
fn is_string(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn is_string(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "is_string", params, args, 1)?;
|
||||
Ok(Value::Bool(matches!(&args[0], Value::String(_))))
|
||||
}
|
||||
@@ -70,7 +70,12 @@ pub fn get_type(value: &Value) -> &str {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_name(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
pub fn type_name(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
_strict: bool,
|
||||
) -> Result<Value> {
|
||||
ensure_args_count(span, "type_name", params, args, 1)?;
|
||||
Ok(Value::String(get_type(&args[0]).into()))
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ fn two_exp(suffix: &str) -> Option<i32> {
|
||||
})
|
||||
}
|
||||
|
||||
fn parse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn parse(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "units.parse";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let string = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -143,7 +143,7 @@ fn tenb_exp(suffix: &str) -> Option<i32> {
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_bytes(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn parse_bytes(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "units.parse_bytes";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let string = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
@@ -166,12 +166,15 @@ fn parse_bytes(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
|
||||
_ => (string, ""),
|
||||
};
|
||||
|
||||
let v: Value = if number_part.starts_with('.') {
|
||||
let v: Value = match if number_part.starts_with('.') {
|
||||
serde_json::from_str(format!("0{number_part}").as_str())
|
||||
} else {
|
||||
serde_json::from_str(number_part)
|
||||
}
|
||||
.with_context(|| span.error("could not parse number"))?;
|
||||
} {
|
||||
Ok(v) => v,
|
||||
Err(_) if strict => bail!(span.error("could not parse number")),
|
||||
_ => return Ok(Value::Undefined),
|
||||
};
|
||||
|
||||
let mut n = match v {
|
||||
Value::Number(n) => n.clone(),
|
||||
|
||||
@@ -71,6 +71,10 @@ impl Engine {
|
||||
&self.modules
|
||||
}
|
||||
|
||||
pub fn set_strict_builtin_errors(&mut self, b: bool) {
|
||||
self.interpreter.set_strict_builtin_errors(b)
|
||||
}
|
||||
|
||||
fn prepare_for_eval(&mut self, enable_tracing: bool) -> Result<()> {
|
||||
self.interpreter.set_traces(enable_tracing);
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ pub struct Interpreter {
|
||||
no_rules_lookup: bool,
|
||||
traces: Option<Vec<std::rc::Rc<str>>>,
|
||||
allow_deprecated: bool,
|
||||
strict_builtin_errors: bool,
|
||||
}
|
||||
|
||||
impl Default for Interpreter {
|
||||
@@ -130,6 +131,7 @@ impl Interpreter {
|
||||
no_rules_lookup: false,
|
||||
traces: None,
|
||||
allow_deprecated: true,
|
||||
strict_builtin_errors: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,14 +170,18 @@ impl Interpreter {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn set_strict_builtin_errors(&mut self, b: bool) {
|
||||
self.strict_builtin_errors = b;
|
||||
}
|
||||
|
||||
pub fn set_input(&mut self, input: Value) {
|
||||
self.input = input;
|
||||
info!("input: {:#?}", self.input);
|
||||
}
|
||||
|
||||
pub fn init_with_document(&mut self) -> Result<()> {
|
||||
*Self::make_or_get_value_mut(&mut self.with_document, &["data"])? = Value::new_object();
|
||||
*Self::make_or_get_value_mut(&mut self.with_document, &["input"])? = Value::new_object();
|
||||
*Self::make_or_get_value_mut(&mut self.with_document, &["data"])? = self.init_data.clone();
|
||||
*Self::make_or_get_value_mut(&mut self.with_document, &["input"])? = self.input.clone();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -789,6 +795,7 @@ impl Interpreter {
|
||||
// OPA raises the error sometimes in static scenarios, but doesn't
|
||||
// raise in scenarios due to data/input
|
||||
(Expr::Array { .. }, _) | (Expr::Object { .. }, _) => Ok(false),
|
||||
|
||||
_ => {
|
||||
let expr_value = self.lookup_or_eval_expr(cache, expr)?;
|
||||
if expr_value == Value::Undefined {
|
||||
@@ -1096,18 +1103,28 @@ impl Interpreter {
|
||||
};
|
||||
|
||||
if path[0] == "input" || path[0] == "data" {
|
||||
if path.len() > 1 {
|
||||
let vref =
|
||||
Self::make_or_get_value_mut(&mut self.with_document, &path[0..1])?;
|
||||
match *vref {
|
||||
Value::Object(_) => (),
|
||||
_ => *vref = Value::new_object(),
|
||||
}
|
||||
}
|
||||
|
||||
*Self::make_or_get_value_mut(&mut self.with_document, &path[..])? = value;
|
||||
} /* else if path.len() == 1 {
|
||||
// TODO: handle var in current module.
|
||||
} else {
|
||||
// TODO: error about input, data
|
||||
} */
|
||||
}
|
||||
|
||||
/* else if path.len() == 1 {
|
||||
// TODO: handle var in current module.
|
||||
} else {
|
||||
// TODO: error about input, data
|
||||
} */
|
||||
}
|
||||
|
||||
self.data = self.with_document["data"].clone();
|
||||
self.input = self.with_document["input"].clone();
|
||||
self.processed.clear();
|
||||
|
||||
(with_document, input, data, processed, with_functions)
|
||||
} else {
|
||||
(
|
||||
@@ -1651,7 +1668,7 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
let v = builtin.0(span, params, &args[..])?;
|
||||
let v = builtin.0(span, params, &args[..], self.strict_builtin_errors)?;
|
||||
|
||||
// Handle trace function.
|
||||
// TODO: with modifier.
|
||||
@@ -1694,18 +1711,29 @@ impl Interpreter {
|
||||
_ => orig_fcn_path.clone(),
|
||||
};
|
||||
|
||||
let empty = vec![];
|
||||
let fcns_rules = match self.lookup_function_by_name(&fcn_path) {
|
||||
Some(r) => r,
|
||||
_ => {
|
||||
if self.default_rules.get(&fcn_path).is_some()
|
||||
|| self
|
||||
.default_rules
|
||||
.get(&get_path_string(fcn, Some(&self.current_module_path))?)
|
||||
.is_some()
|
||||
{
|
||||
// process default functions later.
|
||||
&empty
|
||||
}
|
||||
// Look up builtin function.
|
||||
if let Ok(Some(builtin)) = self.lookup_builtin(span, &fcn_path) {
|
||||
else if let Ok(Some(builtin)) = self.lookup_builtin(span, &fcn_path) {
|
||||
let r = self.eval_builtin_call(span, &fcn_path.clone(), *builtin, params);
|
||||
if orig_fcn_path != fcn_path {
|
||||
self.with_functions.insert(orig_fcn_path, fcn_path);
|
||||
}
|
||||
return r;
|
||||
} else {
|
||||
bail!(span.error(format!("could not find function {fcn_path}").as_str()));
|
||||
}
|
||||
bail!(span.error(format!("could not find function {fcn_path}").as_str()));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1757,10 +1785,10 @@ impl Interpreter {
|
||||
let mut type_match = BTreeSet::new();
|
||||
|
||||
for (idx, a) in args.iter().enumerate() {
|
||||
if self
|
||||
.make_bindings(false, &mut type_match, &mut cache, a, ¶m_values[idx])
|
||||
.is_err()
|
||||
{
|
||||
let b =
|
||||
self.make_bindings(false, &mut type_match, &mut cache, a, ¶m_values[idx]);
|
||||
|
||||
if b.ok() != Some(true) {
|
||||
self.scopes = scopes;
|
||||
continue 'outer;
|
||||
}
|
||||
@@ -1799,7 +1827,9 @@ impl Interpreter {
|
||||
|
||||
// If the function execution resulted in undefined, then propagate it.
|
||||
Value::Undefined => Value::Undefined,
|
||||
_ => bail!("internal error: function did not return set {value:?}"),
|
||||
|
||||
// Function returned a non set value
|
||||
v => v.clone(),
|
||||
};
|
||||
|
||||
// Restore local variables for current context.
|
||||
@@ -1810,6 +1840,34 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
if results.is_empty() {
|
||||
// Back up local variables of current function and empty
|
||||
// the local variables of callee function.
|
||||
let scopes = std::mem::take(&mut self.scopes);
|
||||
if errors.is_empty() {
|
||||
// Check if any default rules can be evaluated.
|
||||
// TODO: with mod
|
||||
let rules = match self.default_rules.get(&fcn_path).cloned() {
|
||||
Some(rules) => Some(rules),
|
||||
None => {
|
||||
let fcn_path = get_path_string(fcn, Some(&self.current_module_path))?;
|
||||
self.default_rules.get(&fcn_path).cloned()
|
||||
}
|
||||
};
|
||||
if let Some(rules) = rules {
|
||||
for (rule, _) in rules.iter() {
|
||||
if let Rule::Default { value, .. } = rule.as_ref() {
|
||||
match self.eval_expr(value) {
|
||||
Ok(v) => results.push(v),
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.scopes = scopes;
|
||||
}
|
||||
|
||||
if results.is_empty() {
|
||||
if errors.is_empty() {
|
||||
return Ok(Value::Undefined);
|
||||
@@ -1937,15 +1995,28 @@ impl Interpreter {
|
||||
return Ok(v);
|
||||
}
|
||||
|
||||
// Evaluate rule corresponding to longest matching path.
|
||||
for i in (1..fields.len() + 1).rev() {
|
||||
if fields.is_empty() {
|
||||
bail!(span.error("this results in recursive evaluation of data."))
|
||||
}
|
||||
|
||||
// Find the rule to which the var being looked up corresponds to. This is the prefix for
|
||||
// which rules exist.
|
||||
let mut found = false;
|
||||
for i in 1..fields.len() + 1 {
|
||||
let path = "data.".to_owned() + &fields[0..i].join(".");
|
||||
if self.rules.get(&path).is_some() || self.default_rules.get(&path).is_some() {
|
||||
self.ensure_rule_evaluated(path)?;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: emit this error only if the var can belong to a rego module; not data specified via json/yaml.
|
||||
//if !no_error && !found {
|
||||
// bail!(span.error("var is unsafe"));
|
||||
//}
|
||||
let _ = found;
|
||||
|
||||
Ok(Self::get_value_chained(self.data.clone(), fields))
|
||||
} else if !self.modules.is_empty() {
|
||||
let path = Parser::get_path_ref_components(&self.module.clone().unwrap().package.refr)?;
|
||||
@@ -1960,10 +2031,8 @@ impl Interpreter {
|
||||
return Ok(v);
|
||||
}
|
||||
|
||||
// Add module prefix and ensure that any matching rule is evaluated.
|
||||
let module_path =
|
||||
Self::get_path_string(&self.current_module()?.package.refr, Some("data"))?;
|
||||
let rule_path = module_path + "." + name.text();
|
||||
// Ensure that all the rules having common prefix (name) are evaluated.
|
||||
let rule_path = "data.".to_owned() + &path.join(".");
|
||||
|
||||
if !no_error
|
||||
&& self.rules.get(&rule_path).is_none()
|
||||
@@ -1971,6 +2040,7 @@ impl Interpreter {
|
||||
{
|
||||
bail!(span.error("var is unsafe"));
|
||||
}
|
||||
|
||||
self.ensure_rule_evaluated(rule_path)?;
|
||||
|
||||
let value = Self::get_value_chained(self.data.clone(), &path[..]);
|
||||
@@ -2318,9 +2388,18 @@ impl Interpreter {
|
||||
}
|
||||
|
||||
if let Rule::Default {
|
||||
span, refr, value, ..
|
||||
span,
|
||||
refr,
|
||||
value,
|
||||
args,
|
||||
..
|
||||
} = rule.as_ref()
|
||||
{
|
||||
if !args.is_empty() {
|
||||
// Non-zero function defaults are evaluated differently.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let scopes = std::mem::take(&mut self.scopes);
|
||||
|
||||
let mut path =
|
||||
@@ -2588,8 +2667,10 @@ impl Interpreter {
|
||||
Expr::RefBrack { refr, .. } => refr,
|
||||
_ => refr,
|
||||
};
|
||||
let path = Self::get_path_string(refr, None)?;
|
||||
let path = self.current_module_path.clone() + "." + &path;
|
||||
//let path = Self::get_path_string(refr, None)?;
|
||||
let path = get_root_var(refr)?;
|
||||
let path = path.text();
|
||||
let path = self.current_module_path.clone() + "." + path;
|
||||
match self.rules.entry(path) {
|
||||
Entry::Occupied(o) => {
|
||||
o.into_mut().push(rule.clone());
|
||||
@@ -2617,8 +2698,10 @@ impl Interpreter {
|
||||
_ => (refr, None),
|
||||
};
|
||||
|
||||
let path = Self::get_path_string(refr, None)?;
|
||||
let path = self.current_module_path.clone() + "." + &path;
|
||||
//let path = Self::get_path_string(refr, None)?;
|
||||
let path = get_root_var(refr)?;
|
||||
let path = path.text();
|
||||
let path = self.current_module_path.clone() + "." + path;
|
||||
match self.default_rules.entry(path) {
|
||||
Entry::Occupied(o) => {
|
||||
for (_, i) in o.get() {
|
||||
|
||||
@@ -209,7 +209,9 @@ impl<'source> Parser<'source> {
|
||||
match self.tok.0 {
|
||||
TokenKind::Ident
|
||||
if self.is_keyword(*span.text())
|
||||
|| self.is_imported_future_keyword(*span.text()) =>
|
||||
|| (self.is_imported_future_keyword(*span.text())
|
||||
// contains can be the name of a builtin even when a keyword
|
||||
&& *span.text() != "contains") =>
|
||||
{
|
||||
Err(self.source.error(
|
||||
self.tok.1.line,
|
||||
@@ -1425,6 +1427,25 @@ impl<'source> Parser<'source> {
|
||||
self.expect("default", "while parsing default rule")?;
|
||||
let rule_ref = Ref::new(self.parse_rule_ref()?);
|
||||
|
||||
let mut args = vec![];
|
||||
if *self.token_text() == "(" {
|
||||
self.next_token()?;
|
||||
if *self.token_text() != ")" {
|
||||
loop {
|
||||
let arg = self.parse_ident()?;
|
||||
if *arg.text() != "_" && args.iter().any(|a: &Span| *a.text() == *arg.text()) {
|
||||
bail!(arg.error("repeating parameter name"));
|
||||
}
|
||||
args.push(arg);
|
||||
if *self.token_text() == ")" || self.tok.0 == TokenKind::Eof {
|
||||
break;
|
||||
}
|
||||
self.expect(",", "while parsing default rule parameters")?;
|
||||
}
|
||||
}
|
||||
self.expect(")", "while parsing default rule parameters")?;
|
||||
}
|
||||
|
||||
let op = match *self.token_text() {
|
||||
"=" => AssignOp::Eq,
|
||||
":=" => AssignOp::ColEq,
|
||||
@@ -1443,6 +1464,7 @@ impl<'source> Parser<'source> {
|
||||
Ok(Rule::Default {
|
||||
span,
|
||||
refr: rule_ref,
|
||||
args: args.into_iter().map(|a| Ref::new(Expr::Var(a))).collect(),
|
||||
op,
|
||||
value,
|
||||
})
|
||||
|
||||
100
src/scheduler.rs
100
src/scheduler.rs
@@ -205,7 +205,8 @@ pub fn schedule<Str: Clone + std::cmp::Ord + std::fmt::Debug>(
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Scope {
|
||||
pub locals: BTreeSet<SourceStr>,
|
||||
pub locals: BTreeMap<SourceStr, Span>,
|
||||
pub unscoped: BTreeSet<SourceStr>,
|
||||
pub inputs: BTreeSet<SourceStr>,
|
||||
}
|
||||
|
||||
@@ -269,8 +270,24 @@ fn traverse(expr: &Ref<Expr>, f: &mut dyn FnMut(&Ref<Expr>) -> Result<bool>) ->
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn var_exists(name: &SourceStr, parent_scopes: &[Scope]) -> bool {
|
||||
parent_scopes.iter().rev().any(|s| s.locals.contains(name))
|
||||
fn var_exists(var: &Span, parent_scopes: &[Scope]) -> bool {
|
||||
let name = var.source_str();
|
||||
|
||||
for pscope in parent_scopes.iter().rev() {
|
||||
if pscope.unscoped.contains(&name) {
|
||||
return true;
|
||||
}
|
||||
// Check parent scope vars defined using :=.
|
||||
if let Some(s) = pscope.locals.get(&name) {
|
||||
// Note: Since a rule cannot span multiple files, it is safe to check only
|
||||
// the line numbers.
|
||||
if s.line <= var.line {
|
||||
// The variable was defined in parent scope prior to current comprehension.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn gather_assigned_vars(
|
||||
@@ -285,19 +302,19 @@ fn gather_assigned_vars(
|
||||
|
||||
// Record local var that can shadow input var.
|
||||
Var(v) if can_shadow => {
|
||||
scope.locals.insert(v.source_str());
|
||||
scope.locals.insert(v.source_str(), v.clone());
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
// Record input vars.
|
||||
Var(v) if var_exists(&v.source_str(), parent_scopes) => {
|
||||
Var(v) if var_exists(v, parent_scopes) => {
|
||||
scope.inputs.insert(v.source_str());
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
// Record local var.
|
||||
Var(v) => {
|
||||
scope.locals.insert(v.source_str());
|
||||
scope.unscoped.insert(v.source_str());
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
@@ -309,11 +326,8 @@ fn gather_assigned_vars(
|
||||
|
||||
fn gather_input_vars(expr: &Ref<Expr>, parent_scopes: &[Scope], scope: &mut Scope) -> Result<()> {
|
||||
traverse(expr, &mut |e| match e.as_ref() {
|
||||
Var(v) if var_exists(&v.source_str(), parent_scopes) => {
|
||||
let var = v.source_str();
|
||||
if !scope.locals.contains(&var) {
|
||||
scope.inputs.insert(var);
|
||||
}
|
||||
Var(v) if !scope.unscoped.contains(&v.source_str()) && var_exists(v, parent_scopes) => {
|
||||
scope.inputs.insert(v.source_str());
|
||||
Ok(false)
|
||||
}
|
||||
_ => Ok(true),
|
||||
@@ -322,14 +336,12 @@ fn gather_input_vars(expr: &Ref<Expr>, parent_scopes: &[Scope], scope: &mut Scop
|
||||
|
||||
fn gather_loop_vars(expr: &Ref<Expr>, parent_scopes: &[Scope], scope: &mut Scope) -> Result<()> {
|
||||
traverse(expr, &mut |e| match e.as_ref() {
|
||||
Var(v) if var_exists(&v.source_str(), parent_scopes) => Ok(false),
|
||||
Var(v) if var_exists(v, parent_scopes) => Ok(false),
|
||||
RefBrack { index, .. } => {
|
||||
if let Var(v) = index.as_ref() {
|
||||
if !matches!(*v.text(), "_" | "input" | "data")
|
||||
&& !var_exists(&v.source_str(), parent_scopes)
|
||||
{
|
||||
if !matches!(*v.text(), "_" | "input" | "data") && !var_exists(v, parent_scopes) {
|
||||
// Treat this as an index var.
|
||||
scope.locals.insert(v.source_str());
|
||||
scope.unscoped.insert(v.source_str());
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
@@ -367,7 +379,7 @@ fn gather_vars(
|
||||
|
||||
pub struct Analyzer {
|
||||
packages: BTreeMap<String, Scope>,
|
||||
locals: BTreeMap<Ref<Query>, Scope>,
|
||||
scope_table: BTreeMap<Ref<Query>, Scope>,
|
||||
scopes: Vec<Scope>,
|
||||
order: BTreeMap<Ref<Query>, Vec<u16>>,
|
||||
functions: FunctionTable,
|
||||
@@ -390,7 +402,7 @@ impl Analyzer {
|
||||
pub fn new() -> Analyzer {
|
||||
Analyzer {
|
||||
packages: BTreeMap::new(),
|
||||
locals: BTreeMap::new(),
|
||||
scope_table: BTreeMap::new(),
|
||||
scopes: vec![],
|
||||
order: BTreeMap::new(),
|
||||
functions: FunctionTable::new(),
|
||||
@@ -407,7 +419,7 @@ impl Analyzer {
|
||||
}
|
||||
|
||||
Ok(Schedule {
|
||||
scopes: self.locals,
|
||||
scopes: self.scope_table,
|
||||
order: self.order,
|
||||
})
|
||||
}
|
||||
@@ -421,7 +433,7 @@ impl Analyzer {
|
||||
self.analyze_query(None, None, query, Scope::default())?;
|
||||
|
||||
Ok(Schedule {
|
||||
scopes: self.locals,
|
||||
scopes: self.scope_table,
|
||||
order: self.order,
|
||||
})
|
||||
}
|
||||
@@ -441,7 +453,7 @@ impl Analyzer {
|
||||
..
|
||||
} => get_root_var(refr)?,
|
||||
};
|
||||
scope.locals.insert(var);
|
||||
scope.unscoped.insert(var);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,9 +541,12 @@ impl Analyzer {
|
||||
RuleHead::Set { key, .. } => (key.clone(), None, scope),
|
||||
RuleHead::Func { args, assign, .. } => {
|
||||
for a in args.iter() {
|
||||
if let Var(v) = a.as_ref() {
|
||||
scope.locals.insert(v.source_str());
|
||||
}
|
||||
traverse(a, &mut |e| {
|
||||
if let Var(v) = e.as_ref() {
|
||||
scope.unscoped.insert(v.source_str());
|
||||
}
|
||||
Ok(true)
|
||||
})?;
|
||||
}
|
||||
(None, assign.as_ref().map(|a| a.value.clone()), scope)
|
||||
}
|
||||
@@ -549,7 +564,7 @@ impl Analyzer {
|
||||
for stmt in &query.stmts {
|
||||
match &stmt.literal {
|
||||
Literal::SomeVars { vars, .. } => vars.iter().for_each(|v| {
|
||||
scope.locals.insert(v.source_str());
|
||||
scope.locals.insert(v.source_str(), v.clone());
|
||||
}),
|
||||
Literal::SomeIn {
|
||||
key,
|
||||
@@ -597,8 +612,9 @@ impl Analyzer {
|
||||
}
|
||||
|
||||
// Remove input vars that are shadowed.
|
||||
for v in &scope.locals {
|
||||
for v in scope.locals.keys() {
|
||||
scope.inputs.remove(v);
|
||||
scope.unscoped.remove(v);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -621,7 +637,7 @@ impl Analyzer {
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if scope.locals.contains(&name)
|
||||
if scope.locals.contains_key(&name) || scope.unscoped.contains(&name)
|
||||
/*|| scope.inputs.contains(name) */
|
||||
{
|
||||
if !is_extra_arg {
|
||||
@@ -637,7 +653,7 @@ impl Analyzer {
|
||||
RefBrack { refr, index, .. } => {
|
||||
if let Var(v) = index.as_ref() {
|
||||
let var = v.source_str();
|
||||
if scope.locals.contains(&var) {
|
||||
if scope.locals.contains_key(&var) || scope.unscoped.contains(&var) {
|
||||
let (rb_used_vars, rb_comprs) = Self::gather_used_vars_comprs_index_vars(
|
||||
refr,
|
||||
scope,
|
||||
@@ -681,7 +697,7 @@ impl Analyzer {
|
||||
let compr_scope = match compr.as_ref() {
|
||||
Expr::ArrayCompr { query, term, .. } | Expr::SetCompr { query, term, .. } => {
|
||||
self.analyze_query(None, Some(term.clone()), query, Scope::default())?;
|
||||
self.locals.get(query)
|
||||
self.scope_table.get(query)
|
||||
}
|
||||
Expr::ObjectCompr {
|
||||
query, key, value, ..
|
||||
@@ -692,7 +708,7 @@ impl Analyzer {
|
||||
query,
|
||||
Scope::default(),
|
||||
)?;
|
||||
self.locals.get(query)
|
||||
self.scope_table.get(query)
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
@@ -700,7 +716,7 @@ impl Analyzer {
|
||||
// Record vars used by the comprehension scope.
|
||||
if let Some(compr_scope) = compr_scope {
|
||||
for iv in &compr_scope.inputs {
|
||||
if scope.locals.contains(iv) {
|
||||
if scope.locals.contains_key(iv) || scope.unscoped.contains(iv) {
|
||||
// Record possible first use of current scope's local var.
|
||||
first_use.entry(iv.clone()).or_insert(compr.span().clone());
|
||||
used_vars.push(iv.clone());
|
||||
@@ -727,11 +743,13 @@ impl Analyzer {
|
||||
traverse(expr, &mut |e| match e.as_ref() {
|
||||
Var(v) => {
|
||||
let var = v.source_str();
|
||||
if scope.locals.contains(&var) {
|
||||
if scope.locals.contains_key(&var) {
|
||||
if check_first_use {
|
||||
Self::check_first_use(v, first_use)?;
|
||||
}
|
||||
vars.push(var);
|
||||
} else if scope.unscoped.contains(&var) {
|
||||
vars.push(var);
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
@@ -890,7 +908,7 @@ impl Analyzer {
|
||||
non_vars: &mut Vec<Ref<Expr>>,
|
||||
) -> Result<()> {
|
||||
traverse(expr, &mut |e| match e.as_ref() {
|
||||
Var(v) if scope.locals.contains(&v.source_str()) => {
|
||||
Var(v) if scope.locals.contains_key(&v.source_str()) => {
|
||||
vars.push(v.source_str());
|
||||
Ok(false)
|
||||
}
|
||||
@@ -1012,8 +1030,8 @@ impl Analyzer {
|
||||
let mut extras_scope = Scope::default();
|
||||
gather_assigned_vars(ea, false, &self.scopes, &mut extras_scope)?;
|
||||
|
||||
for var in &extras_scope.locals {
|
||||
scope.locals.insert(var.clone());
|
||||
for var in &extras_scope.unscoped {
|
||||
scope.unscoped.insert(var.clone());
|
||||
}
|
||||
|
||||
// Gather vars being used.
|
||||
@@ -1022,7 +1040,7 @@ impl Analyzer {
|
||||
&mut scope,
|
||||
&mut first_use,
|
||||
&mut definitions,
|
||||
&Some(&extras_scope.locals),
|
||||
&Some(&extras_scope.unscoped),
|
||||
)?;
|
||||
|
||||
self.process_comprs(
|
||||
@@ -1032,8 +1050,8 @@ impl Analyzer {
|
||||
&mut used_vars,
|
||||
)?;
|
||||
|
||||
if !extras_scope.locals.is_empty() {
|
||||
for var in extras_scope.locals {
|
||||
if !extras_scope.unscoped.is_empty() {
|
||||
for var in extras_scope.unscoped {
|
||||
definitions.push(Definition {
|
||||
var,
|
||||
used_vars: used_vars.clone(),
|
||||
@@ -1076,9 +1094,9 @@ impl Analyzer {
|
||||
self.scopes.push(scope.clone());
|
||||
let mut e_scope = Scope::default();
|
||||
if let Some(key) = key {
|
||||
e_scope.locals.insert(key.source_str());
|
||||
e_scope.locals.insert(key.source_str(), key.clone());
|
||||
}
|
||||
e_scope.locals.insert(value.source_str());
|
||||
e_scope.locals.insert(value.source_str(), value.clone());
|
||||
self.scopes.push(e_scope);
|
||||
|
||||
// TODO: mark first use of key, value so that they cannot be := assigned
|
||||
@@ -1112,7 +1130,7 @@ impl Analyzer {
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
self.locals.insert(query.clone(), scope);
|
||||
self.scope_table.insert(query.clone(), scope);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -10,33 +10,50 @@ bitsor
|
||||
bitsshiftleft
|
||||
bitsshiftright
|
||||
bitsxor
|
||||
casts
|
||||
comparisonexpr
|
||||
completedoc
|
||||
compositebasedereference
|
||||
dataderef
|
||||
comprehensions
|
||||
containskeyword
|
||||
cryptohmacequal
|
||||
cryptohmacmd5
|
||||
cryptohmacmd5
|
||||
cryptohmacsha1
|
||||
cryptohmacsha256
|
||||
cryptohmacsha512
|
||||
cryptomd5
|
||||
cryptsha1
|
||||
cryptosha1
|
||||
cryptosha256
|
||||
dataderef
|
||||
disjunction
|
||||
elsekeyword
|
||||
embeddedvirtualdoc
|
||||
evaltermexpr
|
||||
every
|
||||
example
|
||||
fix1863
|
||||
functionerrors
|
||||
functions
|
||||
globmatch
|
||||
globquotemeta
|
||||
helloworld
|
||||
indexing
|
||||
indirectreferences
|
||||
intersection
|
||||
invalidkeyerror
|
||||
jsonfilteridempotent
|
||||
jwtencodesignheadererrors
|
||||
jwtencodesignpayloaderrors
|
||||
nestedreferences
|
||||
objectfilter
|
||||
objectfilteridempotent
|
||||
objectfilternonstringkey
|
||||
objectget
|
||||
objectkeys
|
||||
objectremove
|
||||
objectremoveidempotent
|
||||
objectremovenonstringkey
|
||||
partialdocconstants
|
||||
partialsetdoc
|
||||
rand
|
||||
regexfind
|
||||
@@ -57,6 +74,7 @@ trimprefix
|
||||
trimright
|
||||
trimspace
|
||||
trimsuffix
|
||||
type
|
||||
typebuiltin
|
||||
typenamebuiltin
|
||||
undos
|
||||
|
||||
26
tests/opa.rs
26
tests/opa.rs
@@ -16,18 +16,33 @@ const OPA_REPO: &str = "https://github.com/open-policy-agent/opa";
|
||||
const OPA_BRANCH: &str = "v0.58.0";
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct TestCase {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
data: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
input: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
input_term: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
modules: Option<Vec<String>>,
|
||||
note: String,
|
||||
query: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
sort_bindings: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
want_result: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
skip: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
error: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
traces: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
strict_error: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
want_error: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
want_error_code: Option<String>,
|
||||
}
|
||||
|
||||
@@ -45,11 +60,18 @@ fn eval_test_case(case: &TestCase) -> Result<Value> {
|
||||
if let Some(input) = &case.input {
|
||||
engine.set_input(input.clone());
|
||||
}
|
||||
if let Some(input_term) = &case.input_term {
|
||||
let input = Value::from_json_str(&input_term)?;
|
||||
engine.set_input(input);
|
||||
}
|
||||
if let Some(modules) = &case.modules {
|
||||
for (idx, rego) in modules.iter().enumerate() {
|
||||
engine.add_policy(format!("rego{idx}.rego"), rego.clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
engine.set_strict_builtin_errors(case.strict_error.unwrap_or_default());
|
||||
|
||||
let query_results = engine.eval_query(case.query.clone(), true)?;
|
||||
|
||||
let mut values = vec![];
|
||||
@@ -182,9 +204,11 @@ fn run_opa_tests(opa_tests_dir: String, folders: &[String]) -> Result<()> {
|
||||
println!("\nOPA TESTSUITE STATUS");
|
||||
println!(" {:40} {:4} {:4}", "FOLDER", "PASS", "FAIL");
|
||||
let (mut npass, mut nfail) = (0, 0);
|
||||
let mut passing = vec![];
|
||||
for (dir, (pass, fail)) in status {
|
||||
if fail == 0 {
|
||||
println!("\x1b[32m {dir:40}: {pass:4} {fail:4}\x1b[0m");
|
||||
passing.push(dir);
|
||||
} else {
|
||||
println!("\x1b[31m {dir:40}: {pass:4} {fail:4}\x1b[0m");
|
||||
}
|
||||
@@ -193,6 +217,8 @@ fn run_opa_tests(opa_tests_dir: String, folders: &[String]) -> Result<()> {
|
||||
}
|
||||
println!();
|
||||
|
||||
std::fs::write("target/opa.passing", passing.join("\n"))?;
|
||||
|
||||
if npass == 0 && nfail == 0 {
|
||||
bail!("no matching tests found.");
|
||||
} else if nfail == 0 {
|
||||
|
||||
@@ -569,12 +569,14 @@ fn match_rule(r: &Rule, v: &Value) -> Result<()> {
|
||||
Rule::Default {
|
||||
span,
|
||||
refr,
|
||||
args,
|
||||
op,
|
||||
value,
|
||||
} => {
|
||||
let obj = &v["default"];
|
||||
match_span_opt(span, &obj["span"])?;
|
||||
match_expr(refr, &obj["refr"])?;
|
||||
match_vec(span /*dummy*/, args, &obj["args"])?;
|
||||
match_assign_op(span, op, &obj["op"])?;
|
||||
match_expr(value, &obj["value"])
|
||||
}
|
||||
|
||||
@@ -37,9 +37,12 @@ cases:
|
||||
r = 1
|
||||
rrr = "fun"
|
||||
scopes:
|
||||
- locals: ["p", "y", "q", "x", "a", "b", "idx"]
|
||||
- locals: ["x"]
|
||||
unscoped: ["p", "y", "q", "a", "b", "idx"]
|
||||
inputs: ["r", "rrr"]
|
||||
- locals: ["k", "r1"]
|
||||
- locals: []
|
||||
unscoped: ["k", "r1"]
|
||||
inputs: ["a", "idx", "rrr"]
|
||||
- locals: ["q", "idx1", "t"]
|
||||
- locals: ["q"]
|
||||
unscoped: ["idx1", "t"]
|
||||
inputs: ["rrr"]
|
||||
|
||||
@@ -12,6 +12,7 @@ use std::collections::BTreeSet;
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct Scope {
|
||||
pub locals: BTreeSet<String>,
|
||||
pub unscoped: BTreeSet<String>,
|
||||
pub inputs: BTreeSet<String>,
|
||||
}
|
||||
|
||||
@@ -27,8 +28,11 @@ struct YamlTest {
|
||||
cases: Vec<TestCase>,
|
||||
}
|
||||
|
||||
fn to_string_set(s: &BTreeSet<SourceStr>) -> BTreeSet<String> {
|
||||
s.iter().map(|s| s.to_string()).collect()
|
||||
fn to_string_set<'a, I>(itr: I) -> BTreeSet<String>
|
||||
where
|
||||
I: std::iter::Iterator<Item = &'a SourceStr>,
|
||||
{
|
||||
itr.map(|s| s.to_string()).collect()
|
||||
}
|
||||
|
||||
fn analyze_file(regos: &[String], expected_scopes: &[Scope]) -> Result<()> {
|
||||
@@ -55,8 +59,18 @@ fn analyze_file(regos: &[String], expected_scopes: &[Scope]) -> Result<()> {
|
||||
if idx > expected_scopes.len() {
|
||||
bail!("extra scope generated.")
|
||||
}
|
||||
assert_eq!(to_string_set(&scope.locals), expected_scopes[idx].locals);
|
||||
assert_eq!(to_string_set(&scope.inputs), expected_scopes[idx].inputs);
|
||||
assert_eq!(
|
||||
to_string_set(scope.locals.keys()),
|
||||
expected_scopes[idx].locals
|
||||
);
|
||||
assert_eq!(
|
||||
to_string_set(scope.unscoped.iter()),
|
||||
expected_scopes[idx].unscoped
|
||||
);
|
||||
assert_eq!(
|
||||
to_string_set(scope.inputs.iter()),
|
||||
expected_scopes[idx].inputs
|
||||
);
|
||||
println!("scope {idx} matched.")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user