Improve crate documentation (#111)

- Document QueryResults
- Delete snippets folder
- Document Value

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2024-01-19 14:51:14 -08:00
committed by GitHub
parent f3884e87e5
commit 6eca85b497
17 changed files with 1424 additions and 241 deletions

View File

@@ -27,11 +27,13 @@ jobs:
- name: Clippy
run: cargo clippy --all-targets --no-deps -- -Dwarnings
- name: Run tests
run: cargo test --verbose
run: cargo test -r --verbose
- name: Build (MUSL)
run: cargo build --verbose --all-targets --target x86_64-unknown-linux-musl
- name: Run tests (MUSL)
run: cargo test --verbose --target x86_64-unknown-linux-musl
run: cargo test -r --verbose --target x86_64-unknown-linux-musl
- name: Run tests (ACI)
run: cargo test -r --test aci
- name: Run tests (OPA Conformance)
run: >-
cargo test --test opa -- $(tr '\n' ' ' < tests/opa.passing)
cargo test -r --test opa -- $(tr '\n' ' ' < tests/opa.passing)

View File

@@ -82,6 +82,7 @@ uuid = { version = "1.6.1", features = ["v4", "fast-rng"], optional = true }
jsonschema = { version = "0.17.1", default-features = false, optional = true }
chrono = { version = "0.4.31", optional = true }
chrono-tz = { version = "0.8.5", optional = true }
document-features = "0.2.8"
[dev-dependencies]

View File

@@ -39,6 +39,25 @@ fn main() -> Result<()> {
}
```
Regorus is designed with [Confidential Computing](https://confidentialcomputing.io/about/) in mind. In Confidential Computing environments,
it is important to be able to control exactly what is being run. Regorus allows enabling and disabling various components using cargo
features. By default all features are enabled.
The default build of regorus example program is 6.4M:
```bash
$ cargo build -r --example regorus; strip target/release/examples/regorus; ls -lh target/release/examples/regorus
$ cargo build -r --example regorus; strip target/release/examples/regorus; ls -lh target/release/examples/regorus
-rwxr-xr-x 1 anand staff 6.4M Jan 19 11:23 target/release/examples/regorus*
```
When all features except for `yaml` are disabled, the binary size drops down to 2.9M.
```bash
$ cargo build -r --example regorus --features "yaml" --no-default-features; strip target/release/examples/regorus; ls -lh target/release/examples/regorus
-rwxr-xr-x 1 anand staff 2.9M Jan 19 11:26 target/release/examples/regorus*
```
Regorus passes the [OPA v0.60.0 test-suite](https://www.openpolicyagent.org/docs/latest/ir/#test-suite) barring a few
builtins. See [OPA Conformance](#opa-conformance) below.
@@ -228,6 +247,7 @@ They are captured in the following [github issues](https://github.com/microsoft/
The grammar used by Regorus to parse Rego policies is described in [grammar.md](https://github.com/microsoft/regorus/blob/main/docs/grammar.md)
in both [W3C EBNF](https://www.w3.org/Notation.html) and [RailRoad Diagram](https://en.wikipedia.org/wiki/Syntax_diagram) formats.
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a

239
docs/builtins.md Normal file
View File

@@ -0,0 +1,239 @@
# Built-in Functions
This page lists all the supported Rego built-in functions and the cargo feature that is needed to enable each builtin.
Those builtins that are not need for a specific use of the Regorus crate can be excluded from the binary by not specifying
the corresponding feature. This is useful in Confidential Computing scenarios where
- There needs to be control over what a policy execution can and cannot do.
- There needs to be control over exactly what goes into the [Trusted Computing Base](https://en.wikipedia.org/wiki/Trusted_computing_base).
Currently many builtins are `baked-in`, i.e. there is no way to exclude them from the TCB.
In future, each builtin will be associated with a feature (many builtins could be associated with the same feature).
- [Comparison](https://www.openpolicyagent.org/docs/latest/policy-reference/#comparison)
| Builtin | Feature |
|--------------------------------------------------------------------------------------------------|---------|
| [x == y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-equal) | _ |
| [x > y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-gt) | _ |
| [x >= y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-gte) | _ |
| [x < y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-lt) | _ |
| [x <= y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-lte) | _ |
| [x != y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-neq) | _ |
- [Numbers](https://www.openpolicyagent.org/docs/latest/policy-reference/#numbers)
| Builtin | Feature |
|-----------------------------------------------------------------------------------------------------------------------|---------|
| [abs](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-abs) | _ |
| [ceil](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-ceil) | _ |
| [x / y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-div) | _ |
| [floor](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-floor) | _ |
| [x - y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-minus) | _ |
| [x * y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-mul) | _ |
| [numbers.range](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-numbersrange) | _ |
| [numbers.range_step](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-numbersrange_step) | _ |
| [x + y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-plus) | _ |
| [rand.intn](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-randintn) | _ |
| [x % y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-rem) | _ |
| [round](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-round) | _ |
- [Aggregates](https://www.openpolicyagent.org/docs/latest/policy-reference/#aggregates)
| Builtin | Feature |
|-----------------------------------------------------------------------------------------------------|---------|
| [count](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-count) | _ |
| [max](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-max) | _ |
| [min](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-min) | _ |
| [product](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-product) | _ |
| [sort](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-sort) | _ |
| [sum](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-sum) | _ |
- [Arrays](https://www.openpolicyagent.org/docs/latest/policy-reference/#arrays-2)
| Builtin | Feature |
|-----------------------------------------------------------------------------------------------------------|---------|
| [array.concat](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-array-arrayconcat) | _ |
| [array.reverse](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-array-arrayreverse) | _ |
| [array.slice](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-array-arrayslice) | _ |
- [Sets](https://www.openpolicyagent.org/docs/latest/policy-reference/#sets-2)
| Builtin | Feature |
|---------------------------------------------------------------------------------------------------------|---------|
| [x & y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-sets-and) | _ |
| [intersection](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-sets-intersection) | _ |
| [x - y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-sets-minus) | _ |
| [x \| y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-sets-or) | _ |
| [union](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-sets-union) | _ |
- [Objects](https://www.openpolicyagent.org/docs/latest/policy-reference/#object)
| Builtin | Feature |
|----------------------------------------------------------------------------------------------------------------------|--------------|
| [json.filter](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-jsonfilter) | _ |
| [json.match_schema](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-jsonmatch_schema) | `jsonschema` |
| [json.remove](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-jsonremove) | _ |
| [json.verify_schema](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-jsonverify_schema) | `jsonschema` |
| [object.filter](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectfilter) | _ |
| [object.get](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectget) | _ |
| [object.keys](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectkeys) | _ |
| [object.remove](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectremove) | _ |
| [object.subset](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectsubset) | _ |
| [object.union](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectunion) | _ |
| [object.union_n](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectunion_n) | _ |
- [Strings](https://www.openpolicyagent.org/docs/latest/policy-reference/#strings)
| Builtin | Feature |
|-----------------------------------------------------------------------------------------------------------------------------------|---------|
| [concat](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-concat) | _ |
| [contains](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-contains) | _ |
| [endswith](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-endswith) | _ |
| [format_int](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-format_int) | _ |
| [indexof](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-indexof) | _ |
| [indexof_n](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-indexof_n) | _ |
| [lower](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-lower) | _ |
| [replace](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-replace) | _ |
| [split](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-split) | _ |
| [sprintf](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-sprintf) | _ |
| [startswith](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-startswith) | _ |
| [strings.any_prefix_match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-stringsany_prefix_match) | _ |
| [strings.any_suffix_match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-stringsany_suffix_match) | _ |
| [strings.render_template](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-stringsrender_template) | _ |
| [strings.replace_n](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-stringsreplace_n) | _ |
| [strings.reverse](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-stringsreverse) | _ |
| [substring](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-substring) | _ |
| [trim](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim) | _ |
| [trim_left](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim_left) | _ |
| [trim_prefix](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim_prefix) | _ |
| [trim_right](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim_right) | _ |
| [trim_space](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim_space) | _ |
| [trim_suffix](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim_suffix) | _ |
| [upper](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-upper) | _ |
- [Regex](https://www.openpolicyagent.org/docs/latest/policy-reference/#regex)
| Builtin | Feature |
|-------------------------------------------------------------------------------------------------------------------------------------------------|---------|
| [regex.find_all_string_submatch_n](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexfind_all_string_submatch_n) | `regex` |
| [regex.find_n](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexfind_n) | `regex` |
| [regex.globs_match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexglobs_match) | `regex` |
| [regex.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexis_valid) | `regex` |
| [regex.match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexmatch) | `regex` |
| [regex.replace](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexreplace) | `regex` |
| [regex.split](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexsplit) | `regex` |
| [regex.template_match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regextemplate_match) | `regex` |
- [Glob](https://www.openpolicyagent.org/docs/latest/policy-reference/#regex)
| Builtin | Feature |
|--------------------------------------------------------------------------------------------------------------|---------|
| [glob.match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-glob-globmatch) | `glob` |
| [glob.quote_meta](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-glob-globquote_meta) | `glob` |
- [Bitwise](https://www.openpolicyagent.org/docs/latest/policy-reference/#regex)
| Builtin | Feature |
|------------------------------------------------------------------------------------------------------|---------|
| [bits.and](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitsand) | _ |
| [bits.lsh](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitslsh) | _ |
| [bits.negate](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitsnegate) | _ |
| [bits.or](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitsor) | _ |
| [bits.rsh](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitsrsh) | _ |
| [bits.xor](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitsxor) | _ |
- [Conversions](https://www.openpolicyagent.org/docs/latest/policy-reference/#conversions)
| Builtin | Feature |
|-------|---------|
[to_number](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-conversions-to_number) | _ |
|
- [Units](https://www.openpolicyagent.org/docs/latest/policy-reference/#units)
| Builtin | Feature |
|-------------------------------------------------------------------------------------------------------------------|---------|
| [units.parse](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-units-unitsparse) | _ |
| [units.parse_bytes](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-units-unitsparse_bytes) | _ |
- [Types](https://www.openpolicyagent.org/docs/latest/policy-reference/#types)
| Builtin | Feature |
|------------------------------------------------------------------------------------------------------|---------|
| [is_array](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_array) | _ |
| [is_boolean](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_boolean) | _ |
| [is_null](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_null) | _ |
| [is_number](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_number) | _ |
| [is_object](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_object) | _ |
| [is_set](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_set) | _ |
| [is_string](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_string) | _ |
| [type_name](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-type_name) | _ |
- [Encoding](https://www.openpolicyagent.org/docs/latest/policy-reference/#encoding)
| Builtin | Feature |
|----------------------------------------------------------------------------------------------------------------------------------|-------------|
| [base64.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64is_valid) | `base64` |
| [base64url.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urldecode) | `base64` |
| [base64url.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urlencode) | `base64url` |
| [base64url.encode_no_pad](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urlencode_no_pad) | `base64url` |
| [hex.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-hexdecode) | `hex` |
| [hex.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-hexencode) | `hex` |
| [json.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonis_valid) | _ |
| [json.marshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonmarshal) | _ |
| [json.unmarshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonunmarshal) | _ |
| [urlquery.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlquerydecode) | `urlquery` |
| [urlquery.decode_object](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlquerydecode_object) | `urlquery` |
| [urlquery.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlqueryencode) | `urlquery` |
| [urlquery.encode_object](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlqueryencode_object) | `urlquery` |
| [yaml.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlis_valid) | `yaml` |
| [yaml.marshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlmarshal) | `yaml` |
| [yaml.unmarshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlunmarshal) | `yaml` |
- [Time](https://www.openpolicyagent.org/docs/latest/policy-reference/#time)
| Builtin | Feature |
|----------------------------------------------------------------------------------------------------------------------------|---------|
| ([time.add_date](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeadd_date) | `time` |
| [time.add_date](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeadd_date) | `time` |
| [time.clock](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeclock) | `time` |
| [time.date](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timedate) | `time` |
| [time.diff](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timediff) | `time` |
| [time.format](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeformat) | `time` |
| [time.now_ns](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timenow_ns) | `time` |
| [time.parse_duration_ns](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeparse_duration_ns) | `time` |
| [time.parse_ns](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeparse_ns) | `time` |
| [time.parse_rfc3339_ns](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeparse_rfc3339_ns) | `time` |
| [time.weekday](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeweekday) | `time` |
- [Cryptography](https://www.openpolicyagent.org/docs/latest/policy-reference/#crypto)
| Builtin | Feature |
|---------------------------------------------------------------------------------------------------------------------|----------|
| [crypto.hmac.equal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptohmacequal) | `crypto` |
| [crypto.hmac.md5](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptohmacmd5) | `crypto` |
| [crypto.hmac.sha1](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptohmacsha1) | `crypto` |
| [crypto.hmac.sha256](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptohmacsha256) | `crypto` |
| [crypto.hmac.sha512](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptohmacsha512) | `crypto` |
| [crypto.md5](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptomd5) | `crypto` |
| [crypto.sha1](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptosha1) | `crypto` |
| [crypto.sha256](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptosha256) | `crypto` |
- [Graphs](https://www.openpolicyagent.org/docs/latest/policy-reference/#graph)
| Builtin | Feature |
|---------------------------------------------------------------------------------------------------------------|---------|
| [graph.reachable](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-graph-graphreachable) | `graph` |
| [walk](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-graph-walk) | `graph` |
- [UUID](https://www.openpolicyagent.org/docs/latest/policy-reference/#uuid)
| Builtin | Feature |
|--------------------------------------------------------------------------------------------------------|---------|
| [uuid.parse](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-uuid-uuidparse) | `uuid` |
| [uuid.rfc4122](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-uuid-uuidrfc4122) | `uuid` |
- [Semantic Versions](https://www.openpolicyagent.org/docs/latest/policy-reference/#semver)
| Builtin | Feature |
|----------------------------------------------------------------------------------------------------------------|----------|
| [semver.compare](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-semver-semvercompare) | `semver` |
| [semver.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-semver-semveris_valid) | `semver` |
- [OPA](https://www.openpolicyagent.org/docs/latest/policy-reference/#opa
| Builtin | Feature |
|-----------------------------------------------------------------------------------------------------|---------|
| [opa.runtime](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-opa-oparuntime) | _ |
- [Debugging](https://www.openpolicyagent.org/docs/latest/policy-reference/#opa)
| Builtin | Feature |
|---------------------------------------------------------------------------------|---------|
| [print(...)](https://www.openpolicyagent.org/docs/latest/policy-reference/#opa) | _ |
- [Tracing](https://www.openpolicyagent.org/docs/latest/policy-reference/#tracing)
| Builtin | Feature |
|----------------------------------------------------------------------------------------------|---------|
| [trace](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-tracing-trace) | _ |

View File

@@ -1,23 +0,0 @@
Cpackage play
a := {4}
mydoc(x) := path {
path := "data.play.a"
}
x := [ y |
y := data.play.a | data.play.b with data.play.a as {5} with data.play.b as {6}
]
r := [ m | m := data.play.p with data.play.p as 5 + 6; true ]
allow {
input.x
== 5
input.y == 5
input.y
== 5
}

View File

@@ -28,11 +28,14 @@ fn print(span: &Span, _params: &[Ref<Expr>], args: &[Value], _strict: bool) -> R
let mut msg = String::default();
for a in args {
match a {
Value::Undefined => msg += "<undefined>",
_ => msg += format!("{a}").as_str(),
Value::Undefined => msg += " <undefined>",
Value::String(s) => msg += &format!(" {s}"),
_ => msg += &format!(" {a}"),
};
}
span.message("print", msg.as_str());
if !msg.is_empty() {
println!("{}", &msg[1..]);
}
Ok(Value::Bool(true))
}

View File

@@ -3,14 +3,16 @@
use crate::ast::{Expr, Ref};
use crate::builtins;
#[allow(unused)]
use crate::builtins::utils::{
ensure_args_count, ensure_object, ensure_string, ensure_string_collection,
};
use crate::lexer::Span;
use crate::value::Value;
use std::collections::{BTreeMap, HashMap};
use std::collections::HashMap;
#[allow(unused)]
use anyhow::{anyhow, bail, Context, Result};
pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
@@ -41,11 +43,6 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("json.is_valid", (json_is_valid, 1));
m.insert("json.marshal", (json_marshal, 1));
m.insert("json.unmarshal", (json_unmarshal, 1));
#[cfg(feature = "jsonschema")]
{
m.insert("json.match_schema", (json_match_schema, 2));
m.insert("json.verify_schema", (json_verify_schema, 1));
}
#[cfg(feature = "yaml")]
{
@@ -240,7 +237,7 @@ fn urlquery_decode_object(
Err(_) => bail!(params[0].span().error("not a valid url query")),
};
let mut map = BTreeMap::new();
let mut map = std::collections::BTreeMap::new();
for (k, v) in url.query_pairs() {
let key = Value::String(k.clone().into());
let value = Value::String(v.clone().into());
@@ -382,72 +379,3 @@ fn json_unmarshal(
let json_str = ensure_string(name, &params[0], &args[0])?;
Value::from_json_str(&json_str).with_context(|| span.error("could not deserialize json."))
}
#[cfg(feature = "jsonschema")]
fn compile_json_schema(param: &Ref<Expr>, arg: &Value) -> Result<jsonschema::JSONSchema> {
let schema_str = match arg {
Value::String(schema_str) => schema_str.as_ref().to_string(),
_ => arg.to_json_str()?,
};
if let Ok(schema) = serde_json::from_str(&schema_str) {
match jsonschema::JSONSchema::compile(&schema) {
Ok(schema) => return Ok(schema),
Err(e) => bail!(e.to_string()),
}
}
bail!(param.span().error("not a valid json schema"))
}
#[cfg(feature = "jsonschema")]
fn json_verify_schema(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
strict: bool,
) -> Result<Value> {
let name = "json.verify_schema";
ensure_args_count(span, name, params, args, 1)?;
Ok(Value::from_array(
match compile_json_schema(&params[0], &args[0]) {
Ok(_) => [Value::Bool(true), Value::Null],
Err(e) if strict => bail!(params[0]
.span()
.error(format!("invalid schema: {e}").as_str())),
Err(e) => [Value::Bool(false), Value::String(e.to_string().into())],
}
.to_vec(),
))
}
#[cfg(feature = "jsonschema")]
fn json_match_schema(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
strict: bool,
) -> Result<Value> {
let name = "json.match_schema";
ensure_args_count(span, name, params, args, 2)?;
// The following is expected to succeed.
let document: serde_json::Value = serde_json::from_str(&args[0].to_json_str()?)?;
Ok(Value::from_array(
match compile_json_schema(&params[1], &args[1]) {
Ok(schema) => match schema.validate(&document) {
Ok(_) => [Value::Bool(true), Value::Null],
Err(e) => [
Value::Bool(false),
Value::from_array(e.map(|e| Value::String(e.to_string().into())).collect()),
],
},
Err(e) if strict => bail!(params[1]
.span()
.error(format!("invalid schema: {e}").as_str())),
Err(e) => [Value::Bool(false), Value::String(e.to_string().into())],
}
.to_vec(),
))
}

View File

@@ -23,6 +23,12 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("object.subset", (subset, 2));
m.insert("object.union", (object_union, 2));
m.insert("object.union_n", (object_union_n, 1));
#[cfg(feature = "jsonschema")]
{
m.insert("json.match_schema", (json_match_schema, 2));
m.insert("json.verify_schema", (json_verify_schema, 1));
}
}
fn json_filter_impl(v: &Value, filter: &Value) -> Value {
@@ -382,3 +388,72 @@ fn object_union_n(
Ok(u)
}
#[cfg(feature = "jsonschema")]
fn compile_json_schema(param: &Ref<Expr>, arg: &Value) -> Result<jsonschema::JSONSchema> {
let schema_str = match arg {
Value::String(schema_str) => schema_str.as_ref().to_string(),
_ => arg.to_json_str()?,
};
if let Ok(schema) = serde_json::from_str(&schema_str) {
match jsonschema::JSONSchema::compile(&schema) {
Ok(schema) => return Ok(schema),
Err(e) => bail!(e.to_string()),
}
}
bail!(param.span().error("not a valid json schema"))
}
#[cfg(feature = "jsonschema")]
fn json_verify_schema(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
strict: bool,
) -> Result<Value> {
let name = "json.verify_schema";
ensure_args_count(span, name, params, args, 1)?;
Ok(Value::from_array(
match compile_json_schema(&params[0], &args[0]) {
Ok(_) => [Value::Bool(true), Value::Null],
Err(e) if strict => bail!(params[0]
.span()
.error(format!("invalid schema: {e}").as_str())),
Err(e) => [Value::Bool(false), Value::String(e.to_string().into())],
}
.to_vec(),
))
}
#[cfg(feature = "jsonschema")]
fn json_match_schema(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
strict: bool,
) -> Result<Value> {
let name = "json.match_schema";
ensure_args_count(span, name, params, args, 2)?;
// The following is expected to succeed.
let document: serde_json::Value = serde_json::from_str(&args[0].to_json_str()?)?;
Ok(Value::from_array(
match compile_json_schema(&params[1], &args[1]) {
Ok(schema) => match schema.validate(&document) {
Ok(_) => [Value::Bool(true), Value::Null],
Err(e) => [
Value::Bool(false),
Value::from_array(e.map(|e| Value::String(e.to_string().into())).collect()),
],
},
Err(e) if strict => bail!(params[1]
.span()
.error(format!("invalid schema: {e}").as_str())),
Err(e) => [Value::Bool(false), Value::String(e.to_string().into())],
}
.to_vec(),
))
}

View File

@@ -16,6 +16,7 @@ use std::path::Path;
use anyhow::Result;
/// The Rego evaluation engine.
///
#[derive(Clone)]
pub struct Engine {
modules: Vec<Ref<Module>>,
@@ -31,6 +32,7 @@ impl Default for Engine {
}
impl Engine {
/// Create an instance of [Engine].
pub fn new() -> Self {
Self {
modules: vec![],
@@ -39,6 +41,29 @@ impl Engine {
}
}
/// Add a policy.
///
/// The policy file will be parsed and converted to AST representation.
/// Multiple policy files may be added to the engine.
///
/// * `path`: A filename to be associated with the policy.
/// * `rego`: The rego policy code.
///
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// let mut engine = Engine::new();
///
/// engine.add_policy(
/// "test.rego".to_string(),
/// r#"
/// package test
/// allow = input.user == "root"
/// "#.to_string())?;
/// # Ok(())
/// # }
/// ```
///
pub fn add_policy(&mut self, path: String, rego: String) -> Result<()> {
let source = Source::new(path, rego);
let mut parser = Parser::new(&source)?;
@@ -48,6 +73,22 @@ impl Engine {
Ok(())
}
/// Add a policy from a given file.
///
/// The policy file will be parsed and converted to AST representation.
/// Multiple policy files may be added to the engine.
///
/// * `path`: Path to the policy file (.rego).
///
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// let mut engine = Engine::new();
///
/// engine.add_policy_from_file("tests/aci/framework.rego")?;
/// # Ok(())
/// # }
/// ```
pub fn add_policy_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let source = Source::from_file(path)?;
let mut parser = Parser::new(&source)?;
@@ -56,28 +97,163 @@ impl Engine {
Ok(())
}
/// Set the input document.
///
/// * `input`: Input documented. Typically this [Value] is constructed from JSON or YAML.
///
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// let mut engine = Engine::new();
///
/// let input = Value::from_json_str(r#"
/// {
/// "role" : "admin",
/// "action": "delete"
/// }"#)?;
///
/// engine.set_input(input);
/// # Ok(())
/// # }
/// ```
pub fn set_input(&mut self, input: Value) {
self.interpreter.set_input(input);
}
/// Clear the data document.
///
/// The data document will be reset to an empty object.
///
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// let mut engine = Engine::new();
///
/// engine.clear_data();
///
/// // Evaluate data.
/// let results = engine.eval_query("data".to_string(), false)?;
///
/// // Assert that it is empty object.
/// assert_eq!(results.result.len(), 1);
/// assert_eq!(results.result[0].expressions.len(), 1);
/// assert_eq!(results.result[0].expressions[0].value, Value::new_object());
/// # Ok(())
/// # }
/// ```
pub fn clear_data(&mut self) {
self.interpreter.set_data(Value::new_object());
self.prepared = false;
}
/// Add data document.
///
/// The specified data document is merged into existing data document.
///
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// let mut engine = Engine::new();
///
/// // Only objects can be added.
/// assert!(engine.add_data(Value::from_json_str("[]")?).is_err());
///
/// // Merge { "x" : 1, "y" : {} }
/// assert!(engine.add_data(Value::from_json_str(r#"{ "x" : 1, "y" : {}}"#)?).is_ok());
///
/// // Merge { "z" : 2 }
/// assert!(engine.add_data(Value::from_json_str(r#"{ "z" : 2 }"#)?).is_ok());
///
/// // Merge { "z" : 3 }. Conflict error.
/// assert!(engine.add_data(Value::from_json_str(r#"{ "z" : 3 }"#)?).is_err());
///
/// assert_eq!(
/// engine.eval_query("data".to_string(), false)?.result[0].expressions[0].value,
/// Value::from_json_str(r#"{ "x": 1, "y": {}, "z": 2}"#)?
/// );
/// # Ok(())
/// # }
/// ```
pub fn add_data(&mut self, data: Value) -> Result<()> {
self.prepared = false;
self.interpreter.get_data_mut().merge(data)
}
pub fn get_modules(&mut self) -> &Vec<Ref<Module>> {
&self.modules
}
/// Set whether builtins should raise errors strictly or not.
///
/// Regorus differs from OPA in that by default builtins will
/// raise errors instead of returning Undefined.
///
/// ----
/// **_NOTE:_** Currently not all builtins honor this flag and will always strictly raise errors.
/// ----
pub fn set_strict_builtin_errors(&mut self, b: bool) {
self.interpreter.set_strict_builtin_errors(b)
}
#[doc(hidden)]
pub fn get_modules(&mut self) -> &Vec<Ref<Module>> {
&self.modules
}
/// Evaluate a Rego query.
///
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// let mut engine = Engine::new();
///
/// // Add policies
/// engine.add_policy_from_file("tests/aci/framework.rego")?;
/// engine.add_policy_from_file("tests/aci/api.rego")?;
/// engine.add_policy_from_file("tests/aci/policy.rego")?;
///
/// // Add data document (if any).
/// // If multiple data documents can be added, they will be merged together.
/// engine.add_data(Value::from_json_file("tests/aci/data.json")?)?;
///
/// // At this point the policies and data have been loaded.
/// // Either the same engine can be used to make multiple queries or the engine
/// // can be cloned to avoid having the reload the policies and data.
/// let _clone = engine.clone();
///
/// // Evaluate a query.
/// // Load input and make query.
/// engine.set_input(Value::new_object());
/// let results = engine.eval_query("data.framework.mount_overlay.allowed".to_string(), false)?;
/// assert!(results.result.is_empty());
///
/// // Evaluate query with different inputs.
/// engine.set_input(Value::from_json_file("tests/aci/input.json")?);
/// let results = engine.eval_query("data.framework.mount_overlay.allowed".to_string(), false)?;
/// assert_eq!(results.result[0].expressions[0].value, Value::from(true));
/// # Ok(())
/// # }
pub fn eval_query(&mut self, query: String, enable_tracing: bool) -> Result<QueryResults> {
self.eval_modules(enable_tracing)?;
let query_module = {
let source = Source::new(
"<query_module.rego>".to_owned(),
"package __internal_query_module".to_owned(),
);
Ref::new(Parser::new(&source)?.parse()?)
};
// Parse the query.
let query_source = Source::new("<query.rego>".to_string(), query);
let mut parser = Parser::new(&query_source)?;
let query_node = parser.parse_user_query()?;
let query_schedule = Analyzer::new().analyze_query_snippet(&self.modules, &query_node)?;
self.interpreter.eval_user_query(
&query_module,
&query_node,
&query_schedule,
enable_tracing,
)
}
#[doc(hidden)]
fn prepare_for_eval(&mut self, enable_tracing: bool) -> Result<()> {
self.interpreter.set_traces(enable_tracing);
@@ -110,6 +286,7 @@ impl Engine {
Ok(())
}
#[doc(hidden)]
pub fn eval_rule(
&mut self,
module: &Ref<Module>,
@@ -124,6 +301,7 @@ impl Engine {
Ok(self.interpreter.get_data_mut().clone())
}
#[doc(hidden)]
pub fn eval_modules(&mut self, enable_tracing: bool) -> Result<Value> {
self.prepare_for_eval(enable_tracing)?;
self.interpreter.clean_internal_evaluation_state();
@@ -167,30 +345,4 @@ impl Engine {
self.interpreter.create_rule_prefixes()?;
Ok(self.interpreter.get_data_mut().clone())
}
pub fn eval_query(&mut self, query: String, enable_tracing: bool) -> Result<QueryResults> {
self.eval_modules(false)?;
let query_module = {
let source = Source::new(
"<query_module.rego>".to_owned(),
"package __internal_query_module".to_owned(),
);
Ref::new(Parser::new(&source)?.parse()?)
};
// Parse the query.
let query_source = Source::new("<query.rego>".to_string(), query);
let mut parser = Parser::new(&query_source)?;
let query_node = parser.parse_user_query()?;
let query_schedule = Analyzer::new().analyze_query_snippet(&self.modules, &query_node)?;
let results = self.interpreter.eval_user_query(
&query_module,
&query_node,
&query_schedule,
enable_tracing,
)?;
Ok(results)
}
}

View File

@@ -235,6 +235,7 @@ impl Interpreter {
self.loop_var_values.clear();
self.scopes = vec![Scope::new()];
self.contexts = vec![];
self.rule_values.clear();
}
fn current_module(&self) -> Result<Ref<Module>> {
@@ -347,7 +348,7 @@ impl Interpreter {
&& get_root_var(refr)?.text() == "data"
{
let index = index.to_string();
v = obj[&index].clone();
v = obj[index].clone();
}
return Ok(Self::get_value_chained(v, &path[..]));
}
@@ -1310,6 +1311,13 @@ impl Interpreter {
r
}
fn clear_scope(scope: &mut Scope) {
// Set each value to undefined. This is equivalent to removing the key.
for (_, v) in scope.iter_mut() {
*v = Value::Undefined;
}
}
fn eval_stmts_in_loop(&mut self, stmts: &[&LiteralStmt], loops: &[LoopExpr]) -> Result<bool> {
if loops.is_empty() {
if !stmts.is_empty() {
@@ -1373,9 +1381,8 @@ impl Interpreter {
}
}
// Save the current scope and restore it after evaluating the statements so
// that the effects of the current loop iteration are cleared.
let scope_saved = self.current_scope()?.clone();
// Create a new scope.
self.scopes.push(Scope::default());
let query_result = self.get_current_context()?.result.clone();
match loop_expr_value {
@@ -1401,12 +1408,13 @@ impl Interpreter {
result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result;
}
self.loop_var_values.remove(&loop_expr.expr());
*self.current_scope_mut()? = scope_saved.clone();
Self::clear_scope(self.current_scope_mut()?);
if let Some(ctx) = self.contexts.last_mut() {
ctx.result = query_result.clone();
}
}
self.loop_var_values.remove(&loop_expr.expr());
}
Value::Set(items) => {
for v in items.iter() {
@@ -1424,12 +1432,12 @@ impl Interpreter {
result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result;
}
self.loop_var_values.remove(&loop_expr.expr());
*self.current_scope_mut()? = scope_saved.clone();
Self::clear_scope(self.current_scope_mut()?);
if let Some(ctx) = self.contexts.last_mut() {
ctx.result = query_result.clone();
}
}
self.loop_var_values.remove(&loop_expr.expr());
}
Value::Object(obj) => {
for (k, v) in obj.iter() {
@@ -1445,12 +1453,13 @@ impl Interpreter {
if exec {
result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result;
}
self.loop_var_values.remove(&loop_expr.expr());
*self.current_scope_mut()? = scope_saved.clone();
Self::clear_scope(self.current_scope_mut()?);
if let Some(ctx) = self.contexts.last_mut() {
ctx.result = query_result.clone();
}
}
self.loop_var_values.remove(&loop_expr.expr());
}
Value::Undefined => {
result = false;
@@ -1461,6 +1470,8 @@ impl Interpreter {
}
}
self.scopes.pop();
// Return true if at least on iteration returned true
Ok(result)
}
@@ -1692,7 +1703,7 @@ impl Interpreter {
if result
.expressions
.iter()
.all(|v| v.value != Value::Undefined)
.all(|v| v.value != Value::Undefined && v.value != Value::Bool(false))
&& !result.expressions.is_empty()
{
ctx.results.result.push(result);
@@ -1811,7 +1822,7 @@ impl Interpreter {
if result
.expressions
.iter()
.all(|v| v.value != Value::Undefined)
.all(|v| v.value != Value::Undefined && v.value != Value::Bool(false))
&& !result.expressions.is_empty()
{
ctx.results.result.push(result);
@@ -2027,10 +2038,12 @@ impl Interpreter {
// Handle trace function.
// TODO: with modifier.
if name == "trace" {
if let (Some(traces), Value::String(msg)) = (&mut self.traces, &v) {
traces.push(msg.clone());
return Ok(Value::Bool(true));
};
}
}
if let Some(name) = cache {
self.builtins_cache.insert((name, args), v.clone());
@@ -2374,6 +2387,14 @@ impl Interpreter {
self.eval_rule(&module, rule)?;
}
}
let prev_module = self.set_current_module(Some(module.clone()))?;
for rule in &module.policy {
if !self.processed.contains(rule) {
self.eval_default_rule(rule)?;
}
}
self.set_current_module(prev_module)?;
}
}
Ok(())
@@ -3186,7 +3207,7 @@ impl Interpreter {
self.set_current_module(prev_module)?;
if let Some(r) = results.result.last() {
if r.bindings.is_empty_object()
if matches!(&r.bindings, Value::Object(obj) if obj.is_empty())
&& r.expressions.iter().any(|e| e.value == Value::Bool(false))
{
results = QueryResults::default();

View File

@@ -139,7 +139,7 @@ pub struct Expression {
/// # }
/// ```
///
/// If any expression evaluates to false, then no results are produces.
/// If any expression evaluates to false, then no results are produced.
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
@@ -172,13 +172,96 @@ impl Default for QueryResult {
}
}
/// Results of evaluating a Rego query.
///
/// Generates the same `json` representation as `opa eval`.
///
/// Queries typically produce a single result.
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// // Create engine and evaluate "true; true; false".
/// let results = Engine::new().eval_query("1 + 1".to_string(), false)?;
///
/// assert!(results.result.len() == 1);
/// assert_eq!(results.result[0].expressions[0].value, Value::from(2u64));
/// assert_eq!(results.result[0].expressions[0].text.as_ref(), "1 + 1");
/// # Ok(())
/// # }
/// ```
///
/// If any expression evaluates to false, then no results are produced.
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// // Create engine and evaluate "true; true; false".
/// let results = Engine::new().eval_query("true; true; false".to_string(), false)?;
///
/// assert!(results.result.is_empty());
/// # Ok(())
/// # }
/// ```
///
/// Queries containing loops produce multiple results.
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// let results = Engine::new().eval_query("x = [1, 2, 3][_]".to_string(), false)?;
///
/// // Three results are produced, one of each value of x.
/// assert_eq!(results.result.len(), 3);
///
/// // Assert expressions and bindings of results.
/// assert_eq!(results.result[0].expressions[0].value, Value::Bool(true));
/// assert_eq!(results.result[0].expressions[0].text.as_ref(), "x = [1, 2, 3][_]");
/// assert_eq!(results.result[0].bindings[&Value::from("x")], Value::from(1u64));
///
/// assert_eq!(results.result[1].expressions[0].value, Value::Bool(true));
/// assert_eq!(results.result[1].expressions[0].text.as_ref(), "x = [1, 2, 3][_]");
/// assert_eq!(results.result[1].bindings[&Value::from("x")], Value::from(2u64));
///
/// assert_eq!(results.result[2].expressions[0].value, Value::Bool(true));
/// assert_eq!(results.result[2].expressions[0].text.as_ref(), "x = [1, 2, 3][_]");
/// assert_eq!(results.result[2].bindings[&Value::from("x")], Value::from(3u64));
/// # Ok(())
/// # }
/// ```
///
/// Loop iterations that evaluate to false or undefined don't produce results.
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// let results = Engine::new().eval_query("x = [1, 2, 3][_]; x >= 2".to_string(), false)?;
///
/// // Two results are produced, one for x = 2 and another for x = 3.
/// assert_eq!(results.result.len(), 2);
///
/// // Assert expressions and bindings of results.
/// assert_eq!(results.result[0].expressions[0].value, Value::Bool(true));
/// assert_eq!(results.result[0].expressions[0].text.as_ref(), "x = [1, 2, 3][_]");
/// assert_eq!(results.result[0].expressions[0].value, Value::Bool(true));
/// assert_eq!(results.result[0].expressions[1].text.as_ref(), "x >= 2");
/// assert_eq!(results.result[0].bindings[&Value::from("x")], Value::from(2u64));
///
/// assert_eq!(results.result[1].expressions[0].value, Value::Bool(true));
/// assert_eq!(results.result[1].expressions[0].text.as_ref(), "x = [1, 2, 3][_]");
/// assert_eq!(results.result[1].expressions[0].value, Value::Bool(true));
/// assert_eq!(results.result[1].expressions[1].text.as_ref(), "x >= 2");
/// assert_eq!(results.result[1].bindings[&Value::from("x")], Value::from(3u64));
/// # Ok(())
/// # }
/// ```
///
/// See [QueryResult] for examples of different kinds of results.
#[derive(Debug, Clone, Default, Serialize)]
pub struct QueryResults {
/// Collection of results of evaluting a query.
#[serde(skip_serializing_if = "Vec::is_empty")]
pub result: Vec<QueryResult>,
}
/// Items in `unstable` are likely to change.
#[doc(hidden)]
pub mod unstable {
pub use crate::ast::*;
pub use crate::lexer::*;

View File

@@ -132,6 +132,26 @@ impl From<f64> for Number {
}
impl Number {
pub fn as_u128(&self) -> Option<u128> {
match self {
Big(b) if b.is_integer() => match u128::try_from(&b.d) {
Ok(v) => Some(v),
_ => None,
},
_ => None,
}
}
pub fn as_i128(&self) -> Option<i128> {
match self {
Big(b) if b.is_integer() => match i128::try_from(&b.d) {
Ok(v) => Some(v),
_ => None,
},
_ => None,
}
}
pub fn as_u64(&self) -> Option<u64> {
match self {
Big(b) if b.is_integer() => match u64::try_from(&b.d) {

View File

@@ -3,7 +3,6 @@
use crate::ast::Expr::*;
use crate::ast::*;
use crate::builtins;
use crate::lexer::*;
use crate::utils::*;
@@ -629,6 +628,7 @@ impl Analyzer {
let mut used_vars = vec![];
let mut comprs = vec![];
let full_expr = expr;
std::convert::identity(&full_expr);
traverse(expr, &mut |e| match e.as_ref() {
Var(v) if !matches!(v.text(), "_" | "input" | "data") => {
let name = v.source_str();
@@ -645,16 +645,19 @@ impl Analyzer {
first_use.entry(name).or_insert(v.clone());
}
} else if !scope.inputs.contains(&name) {
match get_path_string(full_expr, None) {
Ok(path)
if builtins::BUILTINS.contains_key(path.as_str())
|| builtins::deprecated::DEPRECATED.contains_key(path.as_str()) => {
#[cfg(feature = "deprecated")]
{
if let Ok(path) = get_path_string(full_expr, None) {
if crate::builtins::BUILTINS.contains_key(path.as_str())
|| crate::builtins::deprecated::DEPRECATED
.contains_key(path.as_str())
{
return Ok(false);
}
_ => bail!(v.error(
format!("use of undefined variable `{name}` is unsafe").as_str()
)),
}
}
bail!(v.error(format!("use of undefined variable `{name}` is unsafe").as_str()));
}
Ok(false)
}

File diff suppressed because it is too large Load Diff

View File

@@ -45,7 +45,7 @@ fn eval_test_case(dir: &Path, case: &TestCase) -> Result<Value> {
let mut values = vec![];
for qr in query_results.result {
values.push(if !qr.bindings.is_empty_object() {
values.push(if !qr.bindings.as_object()?.is_empty() {
qr.bindings.clone()
} else if let Some(v) = qr.expressions.last() {
v.value.clone()
@@ -53,7 +53,7 @@ fn eval_test_case(dir: &Path, case: &TestCase) -> Result<Value> {
Value::Undefined
});
}
let result = Value::from_array(values);
let result = Value::from(values);
// Make result json compatible. (E.g: avoid sets).
Value::from_json_str(&result.to_string())
}

View File

@@ -84,7 +84,7 @@ fn eval_test_case(case: &TestCase) -> Result<Value> {
let mut values = vec![];
for qr in query_results.result {
values.push(if !qr.bindings.is_empty_object() {
values.push(if !qr.bindings.as_object()?.is_empty() {
if case.sort_bindings == Some(true) {
let mut v = qr.bindings.clone();
let bindings = v.as_object_mut()?;
@@ -105,15 +105,15 @@ fn eval_test_case(case: &TestCase) -> Result<Value> {
});
}
let result = Value::from_array(values);
let result = Value::from(values);
// Make result json compatible. (E.g: avoid sets).
Value::from_json_str(&result.to_string())
}
fn json_schema_tests_check(actual: &Value, expected: &Value) -> bool {
// Fetch `x` binding.
let actual = &actual[0][&Value::String("x".into())];
let expected = &expected[0][&Value::String("x".into())];
let actual = &actual[0]["x"];
let expected = &expected[0]["x"];
match (actual, expected) {
(Value::Array(actual), Value::Array(expected))

View File

@@ -15,7 +15,7 @@ fn non_string_key() -> Result<()> {
obj.as_object_mut()?
.insert(Value::from(std::f64::consts::PI), Value::Null);
obj.as_object_mut()?.insert(
Value::from_array(vec![
Value::from(vec![
Value::Bool(true),
Value::Null,
Value::from(std::f64::consts::PI),
@@ -115,14 +115,14 @@ fn value_as_index() -> Result<()> {
fn string_as_index() -> Result<()> {
let obj = Value::from_json_str(r#"{ "a" : 5, "b" : 6 }"#)?;
assert_eq!(&obj["a"], &Value::from(5.0));
assert_eq!(&obj[&"b".to_owned()], &Value::from(6.0));
assert_eq!(&obj["b".to_owned()], &Value::from(6.0));
Ok(())
}
#[test]
fn usize_as_index() -> Result<()> {
assert_eq!(&Value::from_json_str("[1, 2, 3]")?[0], &Value::from(1.0));
assert_eq!(&Value::from_json_str("[1, 2, 3]")?[5], &Value::Undefined);
assert_eq!(&Value::from_json_str("[1, 2, 3]")?[0u64], &Value::from(1.0));
assert_eq!(&Value::from_json_str("[1, 2, 3]")?[5u64], &Value::Undefined);
Ok(())
}
@@ -135,9 +135,6 @@ fn api() -> Result<()> {
assert_eq!(v["a"], Value::from(3.145));
assert_eq!(v.as_object()?.len(), 1);
// Null
assert!(Value::Null.is_null());
let v = Value::new_set();
assert_eq!(v.as_set()?.len(), 0);