mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
- Document Location, Expression, QueryResult (#109)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
9e1e22432d
commit
d39200a52c
21
README.md
21
README.md
@@ -2,11 +2,14 @@
|
||||
|
||||
**Regorus** is
|
||||
|
||||
- *Rego*-*Rus(t)* - A fast, light-weight [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) interpreter written in Rust.
|
||||
- *Rego*-*Rus(t)* - A fast, light-weight [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
|
||||
interpreter written in Rust.
|
||||
- *Rigorous* - A rigorous enforcer of well-defined Rego semantics.
|
||||
|
||||
Regorus is available as a library that can be easily integrated into your Rust projects.
|
||||
|
||||
Here is an example of evaluating a simple Rego policy:
|
||||
|
||||
```rust
|
||||
use anyhow::Result;
|
||||
use regorus::*;
|
||||
@@ -36,12 +39,13 @@ fn main() -> Result<()> {
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## Getting Started
|
||||
|
||||
[examples/regorus](https://github.com/microsoft/regorus/blob/main/examples/regorus.rs) is an example program that shows how to integrate Regorus into your project and evaluate Rego policies.
|
||||
[examples/regorus](https://github.com/microsoft/regorus/blob/main/examples/regorus.rs) is an example program that
|
||||
shows how to integrate Regorus into your project and evaluate Rego policies.
|
||||
|
||||
To build and install it, do
|
||||
|
||||
@@ -94,7 +98,8 @@ 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)):
|
||||
|
||||
```bash
|
||||
$ regorus eval -d examples/example.rego -i examples/input.json data.example
|
||||
@@ -178,7 +183,8 @@ The test driver can be invoked by running:
|
||||
$ cargo test -r --test opa
|
||||
```
|
||||
|
||||
Currently, Regorus passes all the non-builtin specific tests. See [passing tests suites](https://github.com/microsoft/regorus/blob/main/tests/opa.passing).
|
||||
Currently, Regorus passes all the non-builtin specific tests.
|
||||
See [passing tests suites](https://github.com/microsoft/regorus/blob/main/tests/opa.passing).
|
||||
|
||||
The following test suites don't pass fully due to mising builtins:
|
||||
- `cryptoparsersaprivatekeys`
|
||||
@@ -219,7 +225,8 @@ They are captured in the following [github issues](https://github.com/microsoft/
|
||||
|
||||
### Grammar
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
|
||||
@@ -8,12 +8,14 @@ use crate::parser::*;
|
||||
use crate::scheduler::*;
|
||||
use crate::utils::gather_functions;
|
||||
use crate::value::*;
|
||||
use crate::QueryResults;
|
||||
|
||||
use std::convert::AsRef;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
/// The Rego evaluation engine.
|
||||
#[derive(Clone)]
|
||||
pub struct Engine {
|
||||
modules: Vec<Ref<Module>>,
|
||||
@@ -21,6 +23,7 @@ pub struct Engine {
|
||||
prepared: bool,
|
||||
}
|
||||
|
||||
/// Create a default engine.
|
||||
impl Default for Engine {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
|
||||
@@ -9,10 +9,10 @@ use crate::parser::Parser;
|
||||
use crate::scheduler::*;
|
||||
use crate::utils::*;
|
||||
use crate::value::*;
|
||||
use crate::{Expression, Location, QueryResult, QueryResults};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use log::info;
|
||||
use serde::Serialize;
|
||||
use std::collections::btree_map::Entry as BTreeMapEntry;
|
||||
use std::collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap};
|
||||
use std::ops::Bound::*;
|
||||
@@ -73,42 +73,6 @@ impl Default for Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Location {
|
||||
pub row: u16,
|
||||
pub col: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Expression {
|
||||
pub value: Value,
|
||||
pub text: Rc<str>,
|
||||
pub location: Location,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct QueryResult {
|
||||
// Expressions is shown first to match OPA.
|
||||
pub expressions: Vec<Expression>,
|
||||
#[serde(skip_serializing_if = "Value::is_empty_object")]
|
||||
pub bindings: Value,
|
||||
}
|
||||
|
||||
impl Default for QueryResult {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
bindings: Value::new_object(),
|
||||
expressions: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize)]
|
||||
pub struct QueryResults {
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub result: Vec<QueryResult>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Context {
|
||||
key_expr: Option<ExprRef>,
|
||||
|
||||
161
src/lib.rs
161
src/lib.rs
@@ -4,6 +4,8 @@
|
||||
// Use README.md as crate documentation.
|
||||
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
mod ast;
|
||||
mod builtins;
|
||||
mod engine;
|
||||
@@ -16,9 +18,166 @@ mod utils;
|
||||
mod value;
|
||||
|
||||
pub use engine::Engine;
|
||||
pub use interpreter::{QueryResult, QueryResults};
|
||||
pub use value::Value;
|
||||
|
||||
/// Location of an [`Expression`] in a Rego query.
|
||||
///
|
||||
/// ```
|
||||
/// # use regorus::Engine;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// // Create engine and evaluate " \n 1 + 2".
|
||||
/// let results = Engine::new().eval_query(" \n 1 + 2".to_string(), false)?;
|
||||
///
|
||||
/// // Fetch the location for the expression.
|
||||
/// let loc = &results.result[0].expressions[0].location;
|
||||
///
|
||||
/// assert_eq!(loc.row, 2);
|
||||
/// assert_eq!(loc.col, 3);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ````
|
||||
/// See also [`QueryResult`].
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Location {
|
||||
/// Line number. Starts at 1.
|
||||
pub row: u16,
|
||||
/// Column number. Starts at 1.
|
||||
pub col: u16,
|
||||
}
|
||||
|
||||
/// An expression in a Rego query.
|
||||
///
|
||||
/// ```
|
||||
/// # use regorus::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// // Create engine and evaluate "1 + 2".
|
||||
/// let results = Engine::new().eval_query("1 + 2".to_string(), false)?;
|
||||
///
|
||||
/// // Fetch the expression from results.
|
||||
/// let expr = &results.result[0].expressions[0];
|
||||
///
|
||||
/// assert_eq!(expr.value, Value::from(3u64));
|
||||
/// assert_eq!(expr.text.as_ref(), "1 + 2");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
/// See also [`QueryResult`].
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Expression {
|
||||
/// Computed value of the expression.
|
||||
pub value: Value,
|
||||
|
||||
/// The Rego expression.
|
||||
pub text: std::rc::Rc<str>,
|
||||
|
||||
/// Location of the expression in the query string.
|
||||
pub location: Location,
|
||||
}
|
||||
|
||||
/// Result of evaluating a Rego query.
|
||||
///
|
||||
/// A query containing single expression.
|
||||
/// ```
|
||||
/// # use regorus::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// // Create engine and evaluate "1 + 2".
|
||||
/// let results = Engine::new().eval_query("1 + 2".to_string(), false)?;
|
||||
///
|
||||
/// // Fetch the first (sole) result.
|
||||
/// let result = &results.result[0];
|
||||
///
|
||||
/// assert_eq!(result.expressions[0].value, Value::from(3u64));
|
||||
/// assert_eq!(result.expressions[0].text.as_ref(), "1 + 2");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// A query containing multiple expressions.
|
||||
/// ```
|
||||
/// # use regorus::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// // Create engine and evaluate "1 + 2; 3.5 * 4".
|
||||
/// let results = Engine::new().eval_query("1 + 2; 3.55 * 4".to_string(), false)?;
|
||||
///
|
||||
/// // Fetch the first (sole) result.
|
||||
/// let result = &results.result[0];
|
||||
///
|
||||
/// // First expression.
|
||||
/// assert_eq!(result.expressions[0].value, Value::from(3u64));
|
||||
/// assert_eq!(result.expressions[0].text.as_ref(), "1 + 2");
|
||||
///
|
||||
/// // Second expression.
|
||||
/// assert_eq!(result.expressions[1].value, Value::from(14.2));
|
||||
/// assert_eq!(result.expressions[1].text.as_ref(), "3.55 * 4");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Expressions that create bindings (i.e. associate names to values) evaluate to
|
||||
/// either true or false. The value of bindings are available in the `bindings` field.
|
||||
/// ```
|
||||
/// # use regorus::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// // Create engine and evaluate "x = 1; y = x > 0".
|
||||
/// let results = Engine::new().eval_query("x = 1; y = x > 0".to_string(), false)?;
|
||||
///
|
||||
/// // Fetch the first (sole) result.
|
||||
/// let result = &results.result[0];
|
||||
///
|
||||
/// // First expression is true.
|
||||
/// assert_eq!(result.expressions[0].value, Value::from(true));
|
||||
/// assert_eq!(result.expressions[0].text.as_ref(), "x = 1");
|
||||
///
|
||||
/// // Second expression is true.
|
||||
/// assert_eq!(result.expressions[1].value, Value::from(true));
|
||||
/// assert_eq!(result.expressions[1].text.as_ref(), "y = x > 0");
|
||||
///
|
||||
/// // bindings contains the value for each named expession.
|
||||
/// assert_eq!(result.bindings[&Value::from("x")], Value::from(1u64));
|
||||
/// assert_eq!(result.bindings[&Value::from("y")], Value::from(true));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// If any expression evaluates to false, then no results are produces.
|
||||
/// ```
|
||||
/// # 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(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct QueryResult {
|
||||
/// Expressions in the query.
|
||||
///
|
||||
/// Each statement in the query is treated as a separte expression.
|
||||
///
|
||||
pub expressions: Vec<Expression>,
|
||||
|
||||
/// Bindings created in the query.
|
||||
#[serde(skip_serializing_if = "Value::is_empty_object")]
|
||||
pub bindings: Value,
|
||||
}
|
||||
|
||||
impl Default for QueryResult {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
bindings: Value::new_object(),
|
||||
expressions: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize)]
|
||||
pub struct QueryResults {
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub result: Vec<QueryResult>,
|
||||
}
|
||||
|
||||
/// Items in `unstable` are likely to change.
|
||||
pub mod unstable {
|
||||
pub use crate::ast::*;
|
||||
|
||||
18
src/value.rs
18
src/value.rs
@@ -238,6 +238,24 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Value {
|
||||
fn from(b: bool) -> Self {
|
||||
Value::Bool(b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
fn from(s: String) -> Self {
|
||||
Value::String(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Value {
|
||||
fn from(s: &str) -> Self {
|
||||
Value::String(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for Value {
|
||||
fn from(n: u128) -> Self {
|
||||
Value::Number(Number::from(n))
|
||||
|
||||
Reference in New Issue
Block a user