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
|
## 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
|
Check that the regorus example program is working
|
||||||
|
|
||||||
$ target/release/examples/regorus
|
$ regorus
|
||||||
Usage: regorus <COMMAND>
|
Usage: regorus <COMMAND>
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
eval Evaluate a Rego Query
|
eval Evaluate a Rego Query
|
||||||
lex Tokenize a Rego policy
|
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)
|
help Print this message or the help of the given subcommand(s)
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help Print help
|
-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`
|
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
|
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)):
|
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)
|
Finally, evaluate real-world [policies](tests/aci/) used in Azure Container Instances (ACI)
|
||||||
|
|
||||||
target/release/examples/regorus eval -d tests/aci/framework.rego \
|
regorus eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.policy.mount_overlay=x
|
||||||
-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
|
|
||||||
|
|
||||||
|
|
||||||
## ACI Policies
|
## 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`:
|
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 \
|
regorus eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.policy.mount_overlay=x
|
||||||
-b tests/aci \
|
|
||||||
-d tests/aci/data.json \
|
|
||||||
-i tests/aci/input.json \
|
|
||||||
data.framework.mount_overlay=x
|
|
||||||
|
|
||||||
Verify that [OPA](https://github.com/open-policy-agent/opa/releases) produces the same output
|
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
|
## Performance
|
||||||
|
|
||||||
To check how fast Regorus runs on your system, first install a tool like [hyperfine](https://github.com/sharkdp/hyperfine).
|
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,
|
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"
|
$ hyperfine "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
|
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]
|
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
|
Range (min … max): 4.4 ms … 6.0 ms 422 runs
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ enum RegorusCommand {
|
|||||||
verbose: bool,
|
verbose: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Parse q Rego policy.
|
/// Parse a Rego policy.
|
||||||
Parse {
|
Parse {
|
||||||
/// Rego policy file.
|
/// Rego policy file.
|
||||||
file: String,
|
file: String,
|
||||||
|
|||||||
@@ -321,6 +321,7 @@ pub enum Rule {
|
|||||||
Default {
|
Default {
|
||||||
span: Span,
|
span: Span,
|
||||||
refr: Ref<Expr>,
|
refr: Ref<Expr>,
|
||||||
|
args: Vec<Ref<Expr>>,
|
||||||
op: AssignOp,
|
op: AssignOp,
|
||||||
value: Ref<Expr>,
|
value: Ref<Expr>,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
|||||||
m.insert("sum", (sum, 1));
|
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)?;
|
ensure_args_count(span, "count", params, args, 1)?;
|
||||||
|
|
||||||
Ok(Value::from(Number::from(match &args[0] {
|
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)?;
|
ensure_args_count(span, "max", params, args, 1)?;
|
||||||
|
|
||||||
Ok(match &args[0] {
|
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)?;
|
ensure_args_count(span, "min", params, args, 1)?;
|
||||||
|
|
||||||
Ok(match &args[0] {
|
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)?;
|
ensure_args_count(span, "product", params, args, 1)?;
|
||||||
|
|
||||||
let mut v = Number::from(1_u64);
|
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)?;
|
ensure_args_count(span, "sort", params, args, 1)?;
|
||||||
Ok(match &args[0] {
|
Ok(match &args[0] {
|
||||||
Value::Array(a) => {
|
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)?;
|
ensure_args_count(span, "sum", params, args, 1)?;
|
||||||
|
|
||||||
let mut v = Number::from(0_u64);
|
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));
|
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";
|
let name = "array.concat";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let mut v1 = ensure_array(name, ¶ms[0], args[0].clone())?;
|
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))
|
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";
|
let name = "array.reverse";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
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))
|
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";
|
let name = "array.slice";
|
||||||
ensure_args_count(span, name, params, args, 3)?;
|
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));
|
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";
|
let name = "bits.and";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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";
|
let name = "bits.lsh";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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";
|
let name = "bits.negate";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
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";
|
let name = "bits.or";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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";
|
let name = "bits.rsh";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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";
|
let name = "bits.xor";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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));
|
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";
|
let name = "to_number";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
|
|
||||||
let span = params[0].span();
|
let span = params[0].span();
|
||||||
Ok(match &args[0] {
|
Ok(match &args[0] {
|
||||||
|
Value::Null => Value::from(0u64),
|
||||||
Value::Bool(true) => Value::from(1u64),
|
Value::Bool(true) => Value::from(1u64),
|
||||||
Value::Bool(false) => Value::from(0u64),
|
Value::Bool(false) => Value::from(0u64),
|
||||||
Value::Number(_) => args[0].clone(),
|
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));
|
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";
|
let name = "crypto.hmac.equal";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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";
|
let name = "crypto.hmac.md5";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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()))
|
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";
|
let name = "crypto.hmac.sha1";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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()))
|
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";
|
let name = "crypto.hmac.sha256";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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()))
|
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";
|
let name = "crypto.hmac.sha512";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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()))
|
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";
|
let name = "crypto.md5";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
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()))
|
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";
|
let name = "crypto.sha1";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
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()))
|
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";
|
let name = "crypto.sha256";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
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
|
// Symbol analyzer must ensure that vars used by print are defined before
|
||||||
// the print statement. Scheduler must ensure the above constraint.
|
// the print statement. Scheduler must ensure the above constraint.
|
||||||
// Additionally interpreter must allow undefined inputs to print.
|
// 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 {
|
if args.len() > MAX_ARGS as usize {
|
||||||
bail!(span.error("print supports up to 100 arguments"));
|
bail!(span.error("print supports up to 100 arguments"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,13 @@ lazy_static! {
|
|||||||
let mut m : HashMap<&'static str, BuiltinFcn> = HashMap::new();
|
let mut m : HashMap<&'static str, BuiltinFcn> = HashMap::new();
|
||||||
|
|
||||||
m.insert("all", (all, 1));
|
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));
|
m.insert("set_diff", (set_diff, 2));
|
||||||
|
|
||||||
#[cfg(feature = "crypto")]
|
#[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)?;
|
ensure_args_count(span, "all", params, args, 1)?;
|
||||||
|
|
||||||
Ok(Value::Bool(match &args[0] {
|
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)?;
|
ensure_args_count(span, "any", params, args, 1)?;
|
||||||
|
|
||||||
Ok(Value::Bool(match &args[0] {
|
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";
|
let name = "set_diff";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s1 = ensure_set(name, ¶ms[0], args[0].clone())?;
|
let s1 = ensure_set(name, ¶ms[0], args[0].clone())?;
|
||||||
let s2 = ensure_set(name, ¶ms[1], args[1].clone())?;
|
let s2 = ensure_set(name, ¶ms[1], args[1].clone())?;
|
||||||
Ok(Value::from_set(s1.difference(&s2).cloned().collect()))
|
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";
|
let name = "base64.decode";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
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")]
|
#[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";
|
let name = "yaml.is_valid";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
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")]
|
#[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";
|
let name = "yaml.marshal";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
Ok(Value::String(
|
Ok(Value::String(
|
||||||
@@ -58,14 +68,24 @@ fn yaml_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Val
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "yaml")]
|
#[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";
|
let name = "yaml.unmarshal";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let yaml_str = ensure_string(name, ¶ms[0], &args[0])?;
|
let yaml_str = ensure_string(name, ¶ms[0], &args[0])?;
|
||||||
Value::from_yaml_str(&yaml_str).with_context(|| span.error("could not deserialize yaml."))
|
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";
|
let name = "json.is_valid";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
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()))
|
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";
|
let name = "json.marshal";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
Ok(Value::String(
|
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";
|
let name = "json.unmarshal";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let json_str = ensure_string(name, ¶ms[0], &args[0])?;
|
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")))
|
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";
|
let name = "glob.match";
|
||||||
ensure_args_count(span, name, params, args, 3)?;
|
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[..])))
|
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";
|
let name = "glob.quote_meta";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ use std::collections::HashMap;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use lazy_static::lazy_static;
|
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")]
|
#[cfg(feature = "deprecated")]
|
||||||
pub use deprecated::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)?;
|
ensure_args_count(span, "abs", params, args, 1)?;
|
||||||
Ok(Value::from(
|
Ok(Value::from(
|
||||||
ensure_numeric("abs", ¶ms[0], &args[0])?.abs(),
|
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)?;
|
ensure_args_count(span, "ceil", params, args, 1)?;
|
||||||
Ok(Value::from(
|
Ok(Value::from(
|
||||||
ensure_numeric("ceil", ¶ms[0], &args[0])?.ceil(),
|
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)?;
|
ensure_args_count(span, "floor", params, args, 1)?;
|
||||||
Ok(Value::from(
|
Ok(Value::from(
|
||||||
ensure_numeric("floor", ¶ms[0], &args[0])?.floor(),
|
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)?;
|
ensure_args_count(span, "numbers.range", params, args, 2)?;
|
||||||
let v1 = ensure_numeric("numbers.range", ¶ms[0], &args[0].clone())?;
|
let v1 = ensure_numeric("numbers.range", ¶ms[0], &args[0].clone())?;
|
||||||
let v2 = ensure_numeric("numbers.range", ¶ms[1], &args[1].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))
|
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)?;
|
ensure_args_count(span, "round", params, args, 1)?;
|
||||||
Ok(Value::from(
|
Ok(Value::from(
|
||||||
ensure_numeric("round", ¶ms[0], &args[0])?.round(),
|
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";
|
let fcn = "rand.intn";
|
||||||
ensure_args_count(span, fcn, params, args, 2)?;
|
ensure_args_count(span, fcn, params, args, 2)?;
|
||||||
let _ = ensure_string(fcn, ¶ms[0], &args[0])?;
|
let _ = ensure_string(fcn, ¶ms[0], &args[0])?;
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ fn merge_filters(
|
|||||||
Ok(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";
|
let name = "json.filter";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
ensure_object(name, ¶ms[0], args[0].clone())?;
|
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))
|
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";
|
let name = "object.filter";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let mut obj = ensure_object(name, ¶ms[0], args[0].clone())?;
|
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))
|
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";
|
let name = "object.get";
|
||||||
ensure_args_count(span, name, params, args, 3)?;
|
ensure_args_count(span, name, params, args, 3)?;
|
||||||
let obj = ensure_object(name, ¶ms[0], args[0].clone())?;
|
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";
|
let name = "object.keys";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let obj = ensure_object(name, ¶ms[0], args[0].clone())?;
|
let obj = ensure_object(name, ¶ms[0], args[0].clone())?;
|
||||||
Ok(Value::from_set(obj.keys().cloned().collect()))
|
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";
|
let name = "object.remove";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let mut obj = ensure_object(name, ¶ms[0], args[0].clone())?;
|
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";
|
let name = "object.subset";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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));
|
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";
|
let name = "regex.find_all_string_submatch_n";
|
||||||
ensure_args_count(span, name, params, args, 3)?;
|
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";
|
let name = "regex.find_n";
|
||||||
ensure_args_count(span, name, params, args, 3)?;
|
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";
|
let name = "regex.is_valid";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
Ok(ensure_string(name, ¶ms[0], &args[0])
|
Ok(ensure_string(name, ¶ms[0], &args[0])
|
||||||
.map_or(Value::Bool(false), |p| Value::Bool(Regex::new(&p).is_ok())))
|
.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";
|
let name = "regex.match";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let pattern = ensure_string(name, ¶ms[0], &args[0])?;
|
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)))
|
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";
|
let name = "regex.replace";
|
||||||
ensure_args_count(span, name, params, args, 3)?;
|
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";
|
let name = "regex.split";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let pattern = ensure_string(name, ¶ms[0], &args[0])?;
|
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";
|
let name = "regex.template_match";
|
||||||
ensure_args_count(span, name, params, args, 4)?;
|
ensure_args_count(span, name, params, args, 4)?;
|
||||||
let template = ensure_string(name, ¶ms[0], &args[0])?;
|
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));
|
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";
|
let name = "semver.compare";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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))
|
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";
|
let name = "semver.is_valid";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let v = ensure_string(name, ¶ms[0], &args[0])?;
|
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()))
|
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";
|
let name = "intersection";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let set = ensure_set(name, ¶ms[0], args[0].clone())?;
|
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))
|
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";
|
let name = "union";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let set = ensure_set(name, ¶ms[0], args[0].clone())?;
|
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));
|
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";
|
let name = "concat";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let delimiter = ensure_string(name, ¶ms[0], &args[0])?;
|
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()))
|
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";
|
let name = "contains";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
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())))
|
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";
|
let name = "endswith";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
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";
|
let name = "format_int";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let mut n = ensure_numeric(name, ¶ms[0], &args[0])?;
|
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";
|
let name = "indexof";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
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)]
|
#[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";
|
let name = "indexof_n";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
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))
|
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";
|
let name = "lower";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
||||||
Ok(Value::String(s.to_lowercase().into()))
|
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";
|
let name = "replace";
|
||||||
ensure_args_count(span, name, params, args, 3)?;
|
ensure_args_count(span, name, params, args, 3)?;
|
||||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
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()))
|
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";
|
let name = "replace";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
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";
|
let name = "sprintf";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let fmt = ensure_string(name, ¶ms[0], &args[0])?;
|
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()))
|
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";
|
let name = "strings.any_prefix_match";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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";
|
let name = "strings.any_suffix_match";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
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";
|
let name = "startswith";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
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())))
|
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";
|
let name = "trim";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let obj = ensure_object(name, ¶ms[0], args[0].clone())?;
|
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()))
|
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";
|
let name = "reverse";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
||||||
Ok(Value::String(s.chars().rev().collect::<String>().into()))
|
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";
|
let name = "substring";
|
||||||
ensure_args_count(span, name, params, args, 3)?;
|
ensure_args_count(span, name, params, args, 3)?;
|
||||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
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";
|
let name = "trim";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
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()))
|
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";
|
let name = "trim_left";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
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";
|
let name = "trim_prefix";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
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";
|
let name = "trim_right";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
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";
|
let name = "trim_space";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
||||||
Ok(Value::String(s.trim().into()))
|
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";
|
let name = "trim_suffix";
|
||||||
ensure_args_count(span, name, params, args, 2)?;
|
ensure_args_count(span, name, params, args, 2)?;
|
||||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
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";
|
let name = "upper";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let s = ensure_string(name, ¶ms[0], &args[0])?;
|
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));
|
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";
|
let name = "time.now_ns";
|
||||||
ensure_args_count(span, name, params, args, 0)?;
|
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
|
// Symbol analyzer must ensure that vars used by trace are defined before
|
||||||
// the trace statement. Scheduler must ensure the above constraint.
|
// 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";
|
let name = "trace";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let msg = ensure_string(name, ¶ms[0], &args[0])?;
|
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));
|
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)?;
|
ensure_args_count(span, "is_array", params, args, 1)?;
|
||||||
Ok(Value::Bool(matches!(&args[0], Value::Array(_))))
|
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)?;
|
ensure_args_count(span, "is_boolean", params, args, 1)?;
|
||||||
Ok(Value::Bool(matches!(&args[0], Value::Bool(_))))
|
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)?;
|
ensure_args_count(span, "is_null", params, args, 1)?;
|
||||||
Ok(Value::Bool(matches!(&args[0], Value::Null)))
|
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)?;
|
ensure_args_count(span, "is_number", params, args, 1)?;
|
||||||
Ok(Value::Bool(matches!(&args[0], Value::Number(_))))
|
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)?;
|
ensure_args_count(span, "is_object", params, args, 1)?;
|
||||||
Ok(Value::Bool(matches!(&args[0], Value::Object(_))))
|
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)?;
|
ensure_args_count(span, "is_set", params, args, 1)?;
|
||||||
Ok(Value::Bool(matches!(&args[0], Value::Set(_))))
|
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)?;
|
ensure_args_count(span, "is_string", params, args, 1)?;
|
||||||
Ok(Value::Bool(matches!(&args[0], Value::String(_))))
|
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)?;
|
ensure_args_count(span, "type_name", params, args, 1)?;
|
||||||
Ok(Value::String(get_type(&args[0]).into()))
|
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";
|
let name = "units.parse";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let string = ensure_string(name, ¶ms[0], &args[0])?;
|
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";
|
let name = "units.parse_bytes";
|
||||||
ensure_args_count(span, name, params, args, 1)?;
|
ensure_args_count(span, name, params, args, 1)?;
|
||||||
let string = ensure_string(name, ¶ms[0], &args[0])?;
|
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, ""),
|
_ => (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())
|
serde_json::from_str(format!("0{number_part}").as_str())
|
||||||
} else {
|
} else {
|
||||||
serde_json::from_str(number_part)
|
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 {
|
let mut n = match v {
|
||||||
Value::Number(n) => n.clone(),
|
Value::Number(n) => n.clone(),
|
||||||
|
|||||||
@@ -71,6 +71,10 @@ impl Engine {
|
|||||||
&self.modules
|
&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<()> {
|
fn prepare_for_eval(&mut self, enable_tracing: bool) -> Result<()> {
|
||||||
self.interpreter.set_traces(enable_tracing);
|
self.interpreter.set_traces(enable_tracing);
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ pub struct Interpreter {
|
|||||||
no_rules_lookup: bool,
|
no_rules_lookup: bool,
|
||||||
traces: Option<Vec<std::rc::Rc<str>>>,
|
traces: Option<Vec<std::rc::Rc<str>>>,
|
||||||
allow_deprecated: bool,
|
allow_deprecated: bool,
|
||||||
|
strict_builtin_errors: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Interpreter {
|
impl Default for Interpreter {
|
||||||
@@ -130,6 +131,7 @@ impl Interpreter {
|
|||||||
no_rules_lookup: false,
|
no_rules_lookup: false,
|
||||||
traces: None,
|
traces: None,
|
||||||
allow_deprecated: true,
|
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) {
|
pub fn set_input(&mut self, input: Value) {
|
||||||
self.input = input;
|
self.input = input;
|
||||||
info!("input: {:#?}", self.input);
|
info!("input: {:#?}", self.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_with_document(&mut self) -> Result<()> {
|
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, &["data"])? = self.init_data.clone();
|
||||||
*Self::make_or_get_value_mut(&mut self.with_document, &["input"])? = Value::new_object();
|
*Self::make_or_get_value_mut(&mut self.with_document, &["input"])? = self.input.clone();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -789,6 +795,7 @@ impl Interpreter {
|
|||||||
// OPA raises the error sometimes in static scenarios, but doesn't
|
// OPA raises the error sometimes in static scenarios, but doesn't
|
||||||
// raise in scenarios due to data/input
|
// raise in scenarios due to data/input
|
||||||
(Expr::Array { .. }, _) | (Expr::Object { .. }, _) => Ok(false),
|
(Expr::Array { .. }, _) | (Expr::Object { .. }, _) => Ok(false),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
let expr_value = self.lookup_or_eval_expr(cache, expr)?;
|
let expr_value = self.lookup_or_eval_expr(cache, expr)?;
|
||||||
if expr_value == Value::Undefined {
|
if expr_value == Value::Undefined {
|
||||||
@@ -1096,18 +1103,28 @@ impl Interpreter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if path[0] == "input" || path[0] == "data" {
|
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;
|
*Self::make_or_get_value_mut(&mut self.with_document, &path[..])? = value;
|
||||||
} /* else if path.len() == 1 {
|
}
|
||||||
// TODO: handle var in current module.
|
|
||||||
} else {
|
/* else if path.len() == 1 {
|
||||||
// TODO: error about input, data
|
// TODO: handle var in current module.
|
||||||
} */
|
} else {
|
||||||
|
// TODO: error about input, data
|
||||||
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
self.data = self.with_document["data"].clone();
|
self.data = self.with_document["data"].clone();
|
||||||
self.input = self.with_document["input"].clone();
|
self.input = self.with_document["input"].clone();
|
||||||
self.processed.clear();
|
self.processed.clear();
|
||||||
|
|
||||||
(with_document, input, data, processed, with_functions)
|
(with_document, input, data, processed, with_functions)
|
||||||
} else {
|
} 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.
|
// Handle trace function.
|
||||||
// TODO: with modifier.
|
// TODO: with modifier.
|
||||||
@@ -1694,18 +1711,29 @@ impl Interpreter {
|
|||||||
_ => orig_fcn_path.clone(),
|
_ => orig_fcn_path.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let empty = vec![];
|
||||||
let fcns_rules = match self.lookup_function_by_name(&fcn_path) {
|
let fcns_rules = match self.lookup_function_by_name(&fcn_path) {
|
||||||
Some(r) => r,
|
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.
|
// 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);
|
let r = self.eval_builtin_call(span, &fcn_path.clone(), *builtin, params);
|
||||||
if orig_fcn_path != fcn_path {
|
if orig_fcn_path != fcn_path {
|
||||||
self.with_functions.insert(orig_fcn_path, fcn_path);
|
self.with_functions.insert(orig_fcn_path, fcn_path);
|
||||||
}
|
}
|
||||||
return r;
|
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();
|
let mut type_match = BTreeSet::new();
|
||||||
|
|
||||||
for (idx, a) in args.iter().enumerate() {
|
for (idx, a) in args.iter().enumerate() {
|
||||||
if self
|
let b =
|
||||||
.make_bindings(false, &mut type_match, &mut cache, a, ¶m_values[idx])
|
self.make_bindings(false, &mut type_match, &mut cache, a, ¶m_values[idx]);
|
||||||
.is_err()
|
|
||||||
{
|
if b.ok() != Some(true) {
|
||||||
self.scopes = scopes;
|
self.scopes = scopes;
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
}
|
}
|
||||||
@@ -1799,7 +1827,9 @@ impl Interpreter {
|
|||||||
|
|
||||||
// If the function execution resulted in undefined, then propagate it.
|
// If the function execution resulted in undefined, then propagate it.
|
||||||
Value::Undefined => Value::Undefined,
|
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.
|
// 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 results.is_empty() {
|
||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
return Ok(Value::Undefined);
|
return Ok(Value::Undefined);
|
||||||
@@ -1937,15 +1995,28 @@ impl Interpreter {
|
|||||||
return Ok(v);
|
return Ok(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate rule corresponding to longest matching path.
|
if fields.is_empty() {
|
||||||
for i in (1..fields.len() + 1).rev() {
|
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(".");
|
let path = "data.".to_owned() + &fields[0..i].join(".");
|
||||||
if self.rules.get(&path).is_some() || self.default_rules.get(&path).is_some() {
|
if self.rules.get(&path).is_some() || self.default_rules.get(&path).is_some() {
|
||||||
self.ensure_rule_evaluated(path)?;
|
self.ensure_rule_evaluated(path)?;
|
||||||
|
found = true;
|
||||||
break;
|
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))
|
Ok(Self::get_value_chained(self.data.clone(), fields))
|
||||||
} else if !self.modules.is_empty() {
|
} else if !self.modules.is_empty() {
|
||||||
let path = Parser::get_path_ref_components(&self.module.clone().unwrap().package.refr)?;
|
let path = Parser::get_path_ref_components(&self.module.clone().unwrap().package.refr)?;
|
||||||
@@ -1960,10 +2031,8 @@ impl Interpreter {
|
|||||||
return Ok(v);
|
return Ok(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add module prefix and ensure that any matching rule is evaluated.
|
// Ensure that all the rules having common prefix (name) are evaluated.
|
||||||
let module_path =
|
let rule_path = "data.".to_owned() + &path.join(".");
|
||||||
Self::get_path_string(&self.current_module()?.package.refr, Some("data"))?;
|
|
||||||
let rule_path = module_path + "." + name.text();
|
|
||||||
|
|
||||||
if !no_error
|
if !no_error
|
||||||
&& self.rules.get(&rule_path).is_none()
|
&& self.rules.get(&rule_path).is_none()
|
||||||
@@ -1971,6 +2040,7 @@ impl Interpreter {
|
|||||||
{
|
{
|
||||||
bail!(span.error("var is unsafe"));
|
bail!(span.error("var is unsafe"));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ensure_rule_evaluated(rule_path)?;
|
self.ensure_rule_evaluated(rule_path)?;
|
||||||
|
|
||||||
let value = Self::get_value_chained(self.data.clone(), &path[..]);
|
let value = Self::get_value_chained(self.data.clone(), &path[..]);
|
||||||
@@ -2318,9 +2388,18 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Rule::Default {
|
if let Rule::Default {
|
||||||
span, refr, value, ..
|
span,
|
||||||
|
refr,
|
||||||
|
value,
|
||||||
|
args,
|
||||||
|
..
|
||||||
} = rule.as_ref()
|
} = 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 scopes = std::mem::take(&mut self.scopes);
|
||||||
|
|
||||||
let mut path =
|
let mut path =
|
||||||
@@ -2588,8 +2667,10 @@ impl Interpreter {
|
|||||||
Expr::RefBrack { refr, .. } => refr,
|
Expr::RefBrack { refr, .. } => refr,
|
||||||
_ => refr,
|
_ => refr,
|
||||||
};
|
};
|
||||||
let path = Self::get_path_string(refr, None)?;
|
//let path = Self::get_path_string(refr, None)?;
|
||||||
let path = self.current_module_path.clone() + "." + &path;
|
let path = get_root_var(refr)?;
|
||||||
|
let path = path.text();
|
||||||
|
let path = self.current_module_path.clone() + "." + path;
|
||||||
match self.rules.entry(path) {
|
match self.rules.entry(path) {
|
||||||
Entry::Occupied(o) => {
|
Entry::Occupied(o) => {
|
||||||
o.into_mut().push(rule.clone());
|
o.into_mut().push(rule.clone());
|
||||||
@@ -2617,8 +2698,10 @@ impl Interpreter {
|
|||||||
_ => (refr, None),
|
_ => (refr, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let path = Self::get_path_string(refr, None)?;
|
//let path = Self::get_path_string(refr, None)?;
|
||||||
let path = self.current_module_path.clone() + "." + &path;
|
let path = get_root_var(refr)?;
|
||||||
|
let path = path.text();
|
||||||
|
let path = self.current_module_path.clone() + "." + path;
|
||||||
match self.default_rules.entry(path) {
|
match self.default_rules.entry(path) {
|
||||||
Entry::Occupied(o) => {
|
Entry::Occupied(o) => {
|
||||||
for (_, i) in o.get() {
|
for (_, i) in o.get() {
|
||||||
|
|||||||
@@ -209,7 +209,9 @@ impl<'source> Parser<'source> {
|
|||||||
match self.tok.0 {
|
match self.tok.0 {
|
||||||
TokenKind::Ident
|
TokenKind::Ident
|
||||||
if self.is_keyword(*span.text())
|
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(
|
Err(self.source.error(
|
||||||
self.tok.1.line,
|
self.tok.1.line,
|
||||||
@@ -1425,6 +1427,25 @@ impl<'source> Parser<'source> {
|
|||||||
self.expect("default", "while parsing default rule")?;
|
self.expect("default", "while parsing default rule")?;
|
||||||
let rule_ref = Ref::new(self.parse_rule_ref()?);
|
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() {
|
let op = match *self.token_text() {
|
||||||
"=" => AssignOp::Eq,
|
"=" => AssignOp::Eq,
|
||||||
":=" => AssignOp::ColEq,
|
":=" => AssignOp::ColEq,
|
||||||
@@ -1443,6 +1464,7 @@ impl<'source> Parser<'source> {
|
|||||||
Ok(Rule::Default {
|
Ok(Rule::Default {
|
||||||
span,
|
span,
|
||||||
refr: rule_ref,
|
refr: rule_ref,
|
||||||
|
args: args.into_iter().map(|a| Ref::new(Expr::Var(a))).collect(),
|
||||||
op,
|
op,
|
||||||
value,
|
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)]
|
#[derive(Clone, Default, Debug)]
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
pub locals: BTreeSet<SourceStr>,
|
pub locals: BTreeMap<SourceStr, Span>,
|
||||||
|
pub unscoped: BTreeSet<SourceStr>,
|
||||||
pub inputs: BTreeSet<SourceStr>,
|
pub inputs: BTreeSet<SourceStr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,8 +270,24 @@ fn traverse(expr: &Ref<Expr>, f: &mut dyn FnMut(&Ref<Expr>) -> Result<bool>) ->
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn var_exists(name: &SourceStr, parent_scopes: &[Scope]) -> bool {
|
fn var_exists(var: &Span, parent_scopes: &[Scope]) -> bool {
|
||||||
parent_scopes.iter().rev().any(|s| s.locals.contains(name))
|
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(
|
fn gather_assigned_vars(
|
||||||
@@ -285,19 +302,19 @@ fn gather_assigned_vars(
|
|||||||
|
|
||||||
// Record local var that can shadow input var.
|
// Record local var that can shadow input var.
|
||||||
Var(v) if can_shadow => {
|
Var(v) if can_shadow => {
|
||||||
scope.locals.insert(v.source_str());
|
scope.locals.insert(v.source_str(), v.clone());
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record input vars.
|
// 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());
|
scope.inputs.insert(v.source_str());
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record local var.
|
// Record local var.
|
||||||
Var(v) => {
|
Var(v) => {
|
||||||
scope.locals.insert(v.source_str());
|
scope.unscoped.insert(v.source_str());
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,11 +326,8 @@ fn gather_assigned_vars(
|
|||||||
|
|
||||||
fn gather_input_vars(expr: &Ref<Expr>, parent_scopes: &[Scope], scope: &mut Scope) -> Result<()> {
|
fn gather_input_vars(expr: &Ref<Expr>, parent_scopes: &[Scope], scope: &mut Scope) -> Result<()> {
|
||||||
traverse(expr, &mut |e| match e.as_ref() {
|
traverse(expr, &mut |e| match e.as_ref() {
|
||||||
Var(v) if var_exists(&v.source_str(), parent_scopes) => {
|
Var(v) if !scope.unscoped.contains(&v.source_str()) && var_exists(v, parent_scopes) => {
|
||||||
let var = v.source_str();
|
scope.inputs.insert(v.source_str());
|
||||||
if !scope.locals.contains(&var) {
|
|
||||||
scope.inputs.insert(var);
|
|
||||||
}
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
_ => Ok(true),
|
_ => 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<()> {
|
fn gather_loop_vars(expr: &Ref<Expr>, parent_scopes: &[Scope], scope: &mut Scope) -> Result<()> {
|
||||||
traverse(expr, &mut |e| match e.as_ref() {
|
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, .. } => {
|
RefBrack { index, .. } => {
|
||||||
if let Var(v) = index.as_ref() {
|
if let Var(v) = index.as_ref() {
|
||||||
if !matches!(*v.text(), "_" | "input" | "data")
|
if !matches!(*v.text(), "_" | "input" | "data") && !var_exists(v, parent_scopes) {
|
||||||
&& !var_exists(&v.source_str(), parent_scopes)
|
|
||||||
{
|
|
||||||
// Treat this as an index var.
|
// Treat this as an index var.
|
||||||
scope.locals.insert(v.source_str());
|
scope.unscoped.insert(v.source_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
@@ -367,7 +379,7 @@ fn gather_vars(
|
|||||||
|
|
||||||
pub struct Analyzer {
|
pub struct Analyzer {
|
||||||
packages: BTreeMap<String, Scope>,
|
packages: BTreeMap<String, Scope>,
|
||||||
locals: BTreeMap<Ref<Query>, Scope>,
|
scope_table: BTreeMap<Ref<Query>, Scope>,
|
||||||
scopes: Vec<Scope>,
|
scopes: Vec<Scope>,
|
||||||
order: BTreeMap<Ref<Query>, Vec<u16>>,
|
order: BTreeMap<Ref<Query>, Vec<u16>>,
|
||||||
functions: FunctionTable,
|
functions: FunctionTable,
|
||||||
@@ -390,7 +402,7 @@ impl Analyzer {
|
|||||||
pub fn new() -> Analyzer {
|
pub fn new() -> Analyzer {
|
||||||
Analyzer {
|
Analyzer {
|
||||||
packages: BTreeMap::new(),
|
packages: BTreeMap::new(),
|
||||||
locals: BTreeMap::new(),
|
scope_table: BTreeMap::new(),
|
||||||
scopes: vec![],
|
scopes: vec![],
|
||||||
order: BTreeMap::new(),
|
order: BTreeMap::new(),
|
||||||
functions: FunctionTable::new(),
|
functions: FunctionTable::new(),
|
||||||
@@ -407,7 +419,7 @@ impl Analyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(Schedule {
|
Ok(Schedule {
|
||||||
scopes: self.locals,
|
scopes: self.scope_table,
|
||||||
order: self.order,
|
order: self.order,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -421,7 +433,7 @@ impl Analyzer {
|
|||||||
self.analyze_query(None, None, query, Scope::default())?;
|
self.analyze_query(None, None, query, Scope::default())?;
|
||||||
|
|
||||||
Ok(Schedule {
|
Ok(Schedule {
|
||||||
scopes: self.locals,
|
scopes: self.scope_table,
|
||||||
order: self.order,
|
order: self.order,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -441,7 +453,7 @@ impl Analyzer {
|
|||||||
..
|
..
|
||||||
} => get_root_var(refr)?,
|
} => 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::Set { key, .. } => (key.clone(), None, scope),
|
||||||
RuleHead::Func { args, assign, .. } => {
|
RuleHead::Func { args, assign, .. } => {
|
||||||
for a in args.iter() {
|
for a in args.iter() {
|
||||||
if let Var(v) = a.as_ref() {
|
traverse(a, &mut |e| {
|
||||||
scope.locals.insert(v.source_str());
|
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)
|
(None, assign.as_ref().map(|a| a.value.clone()), scope)
|
||||||
}
|
}
|
||||||
@@ -549,7 +564,7 @@ impl Analyzer {
|
|||||||
for stmt in &query.stmts {
|
for stmt in &query.stmts {
|
||||||
match &stmt.literal {
|
match &stmt.literal {
|
||||||
Literal::SomeVars { vars, .. } => vars.iter().for_each(|v| {
|
Literal::SomeVars { vars, .. } => vars.iter().for_each(|v| {
|
||||||
scope.locals.insert(v.source_str());
|
scope.locals.insert(v.source_str(), v.clone());
|
||||||
}),
|
}),
|
||||||
Literal::SomeIn {
|
Literal::SomeIn {
|
||||||
key,
|
key,
|
||||||
@@ -597,8 +612,9 @@ impl Analyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove input vars that are shadowed.
|
// Remove input vars that are shadowed.
|
||||||
for v in &scope.locals {
|
for v in scope.locals.keys() {
|
||||||
scope.inputs.remove(v);
|
scope.inputs.remove(v);
|
||||||
|
scope.unscoped.remove(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -621,7 +637,7 @@ impl Analyzer {
|
|||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if scope.locals.contains(&name)
|
if scope.locals.contains_key(&name) || scope.unscoped.contains(&name)
|
||||||
/*|| scope.inputs.contains(name) */
|
/*|| scope.inputs.contains(name) */
|
||||||
{
|
{
|
||||||
if !is_extra_arg {
|
if !is_extra_arg {
|
||||||
@@ -637,7 +653,7 @@ impl Analyzer {
|
|||||||
RefBrack { refr, index, .. } => {
|
RefBrack { refr, index, .. } => {
|
||||||
if let Var(v) = index.as_ref() {
|
if let Var(v) = index.as_ref() {
|
||||||
let var = v.source_str();
|
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(
|
let (rb_used_vars, rb_comprs) = Self::gather_used_vars_comprs_index_vars(
|
||||||
refr,
|
refr,
|
||||||
scope,
|
scope,
|
||||||
@@ -681,7 +697,7 @@ impl Analyzer {
|
|||||||
let compr_scope = match compr.as_ref() {
|
let compr_scope = match compr.as_ref() {
|
||||||
Expr::ArrayCompr { query, term, .. } | Expr::SetCompr { query, term, .. } => {
|
Expr::ArrayCompr { query, term, .. } | Expr::SetCompr { query, term, .. } => {
|
||||||
self.analyze_query(None, Some(term.clone()), query, Scope::default())?;
|
self.analyze_query(None, Some(term.clone()), query, Scope::default())?;
|
||||||
self.locals.get(query)
|
self.scope_table.get(query)
|
||||||
}
|
}
|
||||||
Expr::ObjectCompr {
|
Expr::ObjectCompr {
|
||||||
query, key, value, ..
|
query, key, value, ..
|
||||||
@@ -692,7 +708,7 @@ impl Analyzer {
|
|||||||
query,
|
query,
|
||||||
Scope::default(),
|
Scope::default(),
|
||||||
)?;
|
)?;
|
||||||
self.locals.get(query)
|
self.scope_table.get(query)
|
||||||
}
|
}
|
||||||
_ => break,
|
_ => break,
|
||||||
};
|
};
|
||||||
@@ -700,7 +716,7 @@ impl Analyzer {
|
|||||||
// Record vars used by the comprehension scope.
|
// Record vars used by the comprehension scope.
|
||||||
if let Some(compr_scope) = compr_scope {
|
if let Some(compr_scope) = compr_scope {
|
||||||
for iv in &compr_scope.inputs {
|
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.
|
// Record possible first use of current scope's local var.
|
||||||
first_use.entry(iv.clone()).or_insert(compr.span().clone());
|
first_use.entry(iv.clone()).or_insert(compr.span().clone());
|
||||||
used_vars.push(iv.clone());
|
used_vars.push(iv.clone());
|
||||||
@@ -727,11 +743,13 @@ impl Analyzer {
|
|||||||
traverse(expr, &mut |e| match e.as_ref() {
|
traverse(expr, &mut |e| match e.as_ref() {
|
||||||
Var(v) => {
|
Var(v) => {
|
||||||
let var = v.source_str();
|
let var = v.source_str();
|
||||||
if scope.locals.contains(&var) {
|
if scope.locals.contains_key(&var) {
|
||||||
if check_first_use {
|
if check_first_use {
|
||||||
Self::check_first_use(v, first_use)?;
|
Self::check_first_use(v, first_use)?;
|
||||||
}
|
}
|
||||||
vars.push(var);
|
vars.push(var);
|
||||||
|
} else if scope.unscoped.contains(&var) {
|
||||||
|
vars.push(var);
|
||||||
}
|
}
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
@@ -890,7 +908,7 @@ impl Analyzer {
|
|||||||
non_vars: &mut Vec<Ref<Expr>>,
|
non_vars: &mut Vec<Ref<Expr>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
traverse(expr, &mut |e| match e.as_ref() {
|
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());
|
vars.push(v.source_str());
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
@@ -1012,8 +1030,8 @@ impl Analyzer {
|
|||||||
let mut extras_scope = Scope::default();
|
let mut extras_scope = Scope::default();
|
||||||
gather_assigned_vars(ea, false, &self.scopes, &mut extras_scope)?;
|
gather_assigned_vars(ea, false, &self.scopes, &mut extras_scope)?;
|
||||||
|
|
||||||
for var in &extras_scope.locals {
|
for var in &extras_scope.unscoped {
|
||||||
scope.locals.insert(var.clone());
|
scope.unscoped.insert(var.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather vars being used.
|
// Gather vars being used.
|
||||||
@@ -1022,7 +1040,7 @@ impl Analyzer {
|
|||||||
&mut scope,
|
&mut scope,
|
||||||
&mut first_use,
|
&mut first_use,
|
||||||
&mut definitions,
|
&mut definitions,
|
||||||
&Some(&extras_scope.locals),
|
&Some(&extras_scope.unscoped),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.process_comprs(
|
self.process_comprs(
|
||||||
@@ -1032,8 +1050,8 @@ impl Analyzer {
|
|||||||
&mut used_vars,
|
&mut used_vars,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !extras_scope.locals.is_empty() {
|
if !extras_scope.unscoped.is_empty() {
|
||||||
for var in extras_scope.locals {
|
for var in extras_scope.unscoped {
|
||||||
definitions.push(Definition {
|
definitions.push(Definition {
|
||||||
var,
|
var,
|
||||||
used_vars: used_vars.clone(),
|
used_vars: used_vars.clone(),
|
||||||
@@ -1076,9 +1094,9 @@ impl Analyzer {
|
|||||||
self.scopes.push(scope.clone());
|
self.scopes.push(scope.clone());
|
||||||
let mut e_scope = Scope::default();
|
let mut e_scope = Scope::default();
|
||||||
if let Some(key) = key {
|
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);
|
self.scopes.push(e_scope);
|
||||||
|
|
||||||
// TODO: mark first use of key, value so that they cannot be := assigned
|
// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,33 +10,50 @@ bitsor
|
|||||||
bitsshiftleft
|
bitsshiftleft
|
||||||
bitsshiftright
|
bitsshiftright
|
||||||
bitsxor
|
bitsxor
|
||||||
|
casts
|
||||||
comparisonexpr
|
comparisonexpr
|
||||||
completedoc
|
completedoc
|
||||||
compositebasedereference
|
compositebasedereference
|
||||||
dataderef
|
comprehensions
|
||||||
|
containskeyword
|
||||||
cryptohmacequal
|
cryptohmacequal
|
||||||
cryptohmacmd5
|
cryptohmacmd5
|
||||||
cryptohmacsha1
|
cryptohmacsha1
|
||||||
cryptohmacsha256
|
cryptohmacsha256
|
||||||
cryptohmacsha512
|
cryptohmacsha512
|
||||||
cryptomd5
|
cryptomd5
|
||||||
cryptsha1
|
cryptosha1
|
||||||
cryptosha256
|
cryptosha256
|
||||||
|
dataderef
|
||||||
|
disjunction
|
||||||
|
elsekeyword
|
||||||
embeddedvirtualdoc
|
embeddedvirtualdoc
|
||||||
evaltermexpr
|
evaltermexpr
|
||||||
|
every
|
||||||
example
|
example
|
||||||
fix1863
|
fix1863
|
||||||
|
functionerrors
|
||||||
|
functions
|
||||||
globmatch
|
globmatch
|
||||||
|
globquotemeta
|
||||||
helloworld
|
helloworld
|
||||||
indexing
|
indexing
|
||||||
|
indirectreferences
|
||||||
intersection
|
intersection
|
||||||
invalidkeyerror
|
invalidkeyerror
|
||||||
jsonfilteridempotent
|
jsonfilteridempotent
|
||||||
|
jwtencodesignheadererrors
|
||||||
|
jwtencodesignpayloaderrors
|
||||||
nestedreferences
|
nestedreferences
|
||||||
|
objectfilter
|
||||||
objectfilteridempotent
|
objectfilteridempotent
|
||||||
objectfilternonstringkey
|
objectfilternonstringkey
|
||||||
|
objectget
|
||||||
|
objectkeys
|
||||||
|
objectremove
|
||||||
objectremoveidempotent
|
objectremoveidempotent
|
||||||
objectremovenonstringkey
|
objectremovenonstringkey
|
||||||
|
partialdocconstants
|
||||||
partialsetdoc
|
partialsetdoc
|
||||||
rand
|
rand
|
||||||
regexfind
|
regexfind
|
||||||
@@ -57,6 +74,7 @@ trimprefix
|
|||||||
trimright
|
trimright
|
||||||
trimspace
|
trimspace
|
||||||
trimsuffix
|
trimsuffix
|
||||||
|
type
|
||||||
typebuiltin
|
typebuiltin
|
||||||
typenamebuiltin
|
typenamebuiltin
|
||||||
undos
|
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";
|
const OPA_BRANCH: &str = "v0.58.0";
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
struct TestCase {
|
struct TestCase {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
data: Option<Value>,
|
data: Option<Value>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
input: Option<Value>,
|
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>>,
|
modules: Option<Vec<String>>,
|
||||||
note: String,
|
note: String,
|
||||||
query: String,
|
query: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
sort_bindings: Option<bool>,
|
sort_bindings: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
want_result: Option<Value>,
|
want_result: Option<Value>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
skip: Option<bool>,
|
skip: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
traces: Option<bool>,
|
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>,
|
want_error: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
want_error_code: Option<String>,
|
want_error_code: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,11 +60,18 @@ fn eval_test_case(case: &TestCase) -> Result<Value> {
|
|||||||
if let Some(input) = &case.input {
|
if let Some(input) = &case.input {
|
||||||
engine.set_input(input.clone());
|
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 {
|
if let Some(modules) = &case.modules {
|
||||||
for (idx, rego) in modules.iter().enumerate() {
|
for (idx, rego) in modules.iter().enumerate() {
|
||||||
engine.add_policy(format!("rego{idx}.rego"), rego.clone())?;
|
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 query_results = engine.eval_query(case.query.clone(), true)?;
|
||||||
|
|
||||||
let mut values = vec![];
|
let mut values = vec![];
|
||||||
@@ -182,9 +204,11 @@ fn run_opa_tests(opa_tests_dir: String, folders: &[String]) -> Result<()> {
|
|||||||
println!("\nOPA TESTSUITE STATUS");
|
println!("\nOPA TESTSUITE STATUS");
|
||||||
println!(" {:40} {:4} {:4}", "FOLDER", "PASS", "FAIL");
|
println!(" {:40} {:4} {:4}", "FOLDER", "PASS", "FAIL");
|
||||||
let (mut npass, mut nfail) = (0, 0);
|
let (mut npass, mut nfail) = (0, 0);
|
||||||
|
let mut passing = vec![];
|
||||||
for (dir, (pass, fail)) in status {
|
for (dir, (pass, fail)) in status {
|
||||||
if fail == 0 {
|
if fail == 0 {
|
||||||
println!("\x1b[32m {dir:40}: {pass:4} {fail:4}\x1b[0m");
|
println!("\x1b[32m {dir:40}: {pass:4} {fail:4}\x1b[0m");
|
||||||
|
passing.push(dir);
|
||||||
} else {
|
} else {
|
||||||
println!("\x1b[31m {dir:40}: {pass:4} {fail:4}\x1b[0m");
|
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!();
|
println!();
|
||||||
|
|
||||||
|
std::fs::write("target/opa.passing", passing.join("\n"))?;
|
||||||
|
|
||||||
if npass == 0 && nfail == 0 {
|
if npass == 0 && nfail == 0 {
|
||||||
bail!("no matching tests found.");
|
bail!("no matching tests found.");
|
||||||
} else if nfail == 0 {
|
} else if nfail == 0 {
|
||||||
|
|||||||
@@ -569,12 +569,14 @@ fn match_rule(r: &Rule, v: &Value) -> Result<()> {
|
|||||||
Rule::Default {
|
Rule::Default {
|
||||||
span,
|
span,
|
||||||
refr,
|
refr,
|
||||||
|
args,
|
||||||
op,
|
op,
|
||||||
value,
|
value,
|
||||||
} => {
|
} => {
|
||||||
let obj = &v["default"];
|
let obj = &v["default"];
|
||||||
match_span_opt(span, &obj["span"])?;
|
match_span_opt(span, &obj["span"])?;
|
||||||
match_expr(refr, &obj["refr"])?;
|
match_expr(refr, &obj["refr"])?;
|
||||||
|
match_vec(span /*dummy*/, args, &obj["args"])?;
|
||||||
match_assign_op(span, op, &obj["op"])?;
|
match_assign_op(span, op, &obj["op"])?;
|
||||||
match_expr(value, &obj["value"])
|
match_expr(value, &obj["value"])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,9 +37,12 @@ cases:
|
|||||||
r = 1
|
r = 1
|
||||||
rrr = "fun"
|
rrr = "fun"
|
||||||
scopes:
|
scopes:
|
||||||
- locals: ["p", "y", "q", "x", "a", "b", "idx"]
|
- locals: ["x"]
|
||||||
|
unscoped: ["p", "y", "q", "a", "b", "idx"]
|
||||||
inputs: ["r", "rrr"]
|
inputs: ["r", "rrr"]
|
||||||
- locals: ["k", "r1"]
|
- locals: []
|
||||||
|
unscoped: ["k", "r1"]
|
||||||
inputs: ["a", "idx", "rrr"]
|
inputs: ["a", "idx", "rrr"]
|
||||||
- locals: ["q", "idx1", "t"]
|
- locals: ["q"]
|
||||||
|
unscoped: ["idx1", "t"]
|
||||||
inputs: ["rrr"]
|
inputs: ["rrr"]
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use std::collections::BTreeSet;
|
|||||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
struct Scope {
|
struct Scope {
|
||||||
pub locals: BTreeSet<String>,
|
pub locals: BTreeSet<String>,
|
||||||
|
pub unscoped: BTreeSet<String>,
|
||||||
pub inputs: BTreeSet<String>,
|
pub inputs: BTreeSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,8 +28,11 @@ struct YamlTest {
|
|||||||
cases: Vec<TestCase>,
|
cases: Vec<TestCase>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string_set(s: &BTreeSet<SourceStr>) -> BTreeSet<String> {
|
fn to_string_set<'a, I>(itr: I) -> BTreeSet<String>
|
||||||
s.iter().map(|s| s.to_string()).collect()
|
where
|
||||||
|
I: std::iter::Iterator<Item = &'a SourceStr>,
|
||||||
|
{
|
||||||
|
itr.map(|s| s.to_string()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_file(regos: &[String], expected_scopes: &[Scope]) -> Result<()> {
|
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() {
|
if idx > expected_scopes.len() {
|
||||||
bail!("extra scope generated.")
|
bail!("extra scope generated.")
|
||||||
}
|
}
|
||||||
assert_eq!(to_string_set(&scope.locals), expected_scopes[idx].locals);
|
assert_eq!(
|
||||||
assert_eq!(to_string_set(&scope.inputs), expected_scopes[idx].inputs);
|
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.")
|
println!("scope {idx} matched.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user