Struct regorus::Engine

source ·
pub struct Engine { /* private fields */ }
Expand description

The Rego evaluation engine.

Implementations§

source§

impl Engine

source

pub fn new() -> Self

Create an instance of Engine.

source

pub fn add_policy(&mut self, path: String, rego: String) -> Result<()>

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.
let mut engine = Engine::new();

engine.add_policy(
   "test.rego".to_string(),
   r#"
   package test
   allow = input.user == "root"
   "#.to_string())?;
source

pub fn add_policy_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()>

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).
let mut engine = Engine::new();

engine.add_policy_from_file("tests/aci/framework.rego")?;
source

pub fn set_input(&mut self, input: Value)

Set the input document.

  • input: Input documented. Typically this Value is constructed from JSON or YAML.
let mut engine = Engine::new();

let input = Value::from_json_str(r#"
{
  "role" : "admin",
  "action": "delete"
}"#)?;

engine.set_input(input);
source

pub fn set_input_json(&mut self, input_json: &str) -> Result<()>

source

pub fn clear_data(&mut self)

Clear the data document.

The data document will be reset to an empty object.

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());
source

pub fn add_data(&mut self, data: Value) -> Result<()>

Add data document.

The specified data document is merged into existing data document.

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}"#)?
);
source

pub fn add_data_json(&mut self, data_json: &str) -> Result<()>

source

pub fn set_strict_builtin_errors(&mut self, b: bool)

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.
source

pub fn eval_rule(&mut self, path: String) -> Result<Value>

Evaluate rule(s) at given path.

Engine::eval_rule is often faster than Engine::eval_query and should be preferred if OPA style QueryResults are not needed.

let mut engine = Engine::new();

// Add policy
engine.add_policy(
  "policy.rego".to_string(),
  r#"
  package example
  import rego.v1

  x = [1, 2]

  y := 5 if input.a > 2
  "#.to_string())?;

// Evaluate rule.
let v = engine.eval_rule("data.example.x".to_string())?;
assert_eq!(v, Value::from(vec![Value::from(1), Value::from(2)]));

// y evaluates to undefined.
let v = engine.eval_rule("data.example.y".to_string())?;
assert_eq!(v, Value::Undefined);

// Evaluating a non-existent rule is an error.
let r = engine.eval_rule("data.exaample.x".to_string());
assert!(r.is_err());

// Path must be valid rule paths.
assert!( engine.eval_rule("data".to_string()).is_err());
assert!( engine.eval_rule("data.example".to_string()).is_err());
source

pub fn eval_query( &mut self, query: String, enable_tracing: bool ) -> Result<QueryResults>

Evaluate a Rego query.

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_eq!(results.result[0].expressions[0].value, Value::from(false));

// 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));
source

pub fn eval_bool_query( &mut self, query: String, enable_tracing: bool ) -> Result<bool>

Evaluate a Rego query that produces a boolean value.

This function should be preferred over Engine::eval_query if just a true/false value is desired instead of QueryResults.


let enable_tracing = false;
assert_eq!(engine.eval_bool_query("1 > 2".to_string(), enable_tracing)?, false);
assert_eq!(engine.eval_bool_query("1 < 2".to_string(), enable_tracing)?, true);

// Non boolean queries will raise an error.
assert!(engine.eval_bool_query("1+1".to_string(), enable_tracing).is_err());

// Queries producing multiple values will raise an error.
assert!(engine.eval_bool_query("true; true".to_string(), enable_tracing).is_err());

// Queries producing no values will raise an error.
assert!(engine.eval_bool_query("true; false; true".to_string(), enable_tracing).is_err());
source

pub fn eval_allow_query(&mut self, query: String, enable_tracing: bool) -> bool

Evaluate an allow query.

This is a wrapper over Engine::eval_bool_query that returns true only if the boolean query succeed and produced a true value.


let enable_tracing = false;
assert_eq!(engine.eval_allow_query("1 > 2".to_string(), enable_tracing), false);
assert_eq!(engine.eval_allow_query("1 < 2".to_string(), enable_tracing), true);

assert_eq!(engine.eval_allow_query("1+1".to_string(), enable_tracing), false);
assert_eq!(engine.eval_allow_query("true; true".to_string(), enable_tracing), false);
assert_eq!(engine.eval_allow_query("true; false; true".to_string(), enable_tracing), false);
source

pub fn eval_deny_query(&mut self, query: String, enable_tracing: bool) -> bool

Evaluate a deny query.

This is a wrapper over Engine::eval_bool_query that returns false only if the boolean query succeed and produced a false value.


let enable_tracing = false;
assert_eq!(engine.eval_deny_query("1 > 2".to_string(), enable_tracing), false);
assert_eq!(engine.eval_deny_query("1 < 2".to_string(), enable_tracing), true);

assert_eq!(engine.eval_deny_query("1+1".to_string(), enable_tracing), true);
assert_eq!(engine.eval_deny_query("true; true".to_string(), enable_tracing), true);
assert_eq!(engine.eval_deny_query("true; false; true".to_string(), enable_tracing), true);
source

pub fn add_extension( &mut self, path: String, nargs: u8, extension: Box<dyn Extension> ) -> Result<()>

Add a custom builtin (extension).

  • path: The fully qualified path of the builtin.
  • nargs: The number of arguments the builtin takes.
  • extension: The Extension instance.
let mut engine = Engine::new();

// Policy uses `do_magic` custom builtin.
engine.add_policy(
   "test.rego".to_string(),
   r#"package test
      x = do_magic(1)
   "#.to_string(),
)?;

// Evaluating fails since `do_magic` is not defined.
assert!(engine.eval_query("data.test.x".to_string(), false).is_err());

// Add extension to implement `do_magic`. The extension can be stateful.
let mut magic = 8;
engine.add_extension("do_magic".to_string(), 1 , Box::new(move | mut params: Vec<Value> | {
  // params is mut and therefore individual values can be removed from it and modified.
  // The number of parameters (1) has already been validated.

  match &params[0].as_i64() {
     Ok(i) => {
        // Compute value
        let v = *i + magic;
        // Update extension state.
        magic += 1;
        Ok(Value::from(v))
     }
     // Extensions can raise errors. Regorus will add location information to
     // the error.
     _ => bail!("do_magic expects i64 value")
  }
}))?;

// Evaluation will now succeed.
let r = engine.eval_query("data.test.x".to_string(), false)?;
assert_eq!(r.result[0].expressions[0].value.as_i64()?, 9);

// Cloning the engine will also clone the extension.
let mut engine1 = engine.clone();

// Evaluating again will return a different value since the extension is stateful.
let r = engine.eval_query("data.test.x".to_string(), false)?;
assert_eq!(r.result[0].expressions[0].value.as_i64()?, 10);

// The second engine has a clone of the extension.
let r = engine1.eval_query("data.test.x".to_string(), false)?;
assert_eq!(r.result[0].expressions[0].value.as_i64()?, 10);

// Once added, the extension cannot be replaced or removed.
assert!(engine.add_extension("do_magic".to_string(), 1, Box::new(|_:Vec<Value>| {
  Ok(Value::Undefined)
})).is_err());

// Extensions don't support out-parameter syntax.
engine.add_policy(
  "policy.rego".to_string(),
  r#"package invalid
     x = y {
      do_magic(2, y)  # y is supplied as an out parameter.
    }
   "#.to_string()
)?;

// Evaluation fails since rule x calls an extension with out parameter.
assert!(engine.eval_query("data.invalid.x".to_string(), false).is_err());
source

pub fn get_coverage_report(&self) -> Result<Report>

Get the coverage report.

let mut engine = Engine::new();

engine.add_policy(
   "policy.rego".to_string(),
   r#"
package test    # Line 2

x = y {         # Line 4
  input.a > 2   # Line 5
  y = 5         # Line 6
}
   "#.to_string()
)?;

// Enable coverage.
engine.set_enable_coverage(true);

engine.eval_query("data".to_string(), false)?;

let report = engine.get_coverage_report()?;
assert_eq!(report.files[0].path, "policy.rego");

// Only line 5 is evaluated.
assert_eq!(report.files[0].covered.iter().cloned().collect::<Vec<u32>>(), vec![5]);

// Line 4 and 6 are not evaluated.
assert_eq!(report.files[0].not_covered.iter().cloned().collect::<Vec<u32>>(), vec![4, 6]);

See also crate::coverage::Report::to_colored_string.

source

pub fn set_enable_coverage(&mut self, enable: bool)

Enable/disable policy coverage.

If enable is different from the current value, then any existing coverage information will be cleared.

source

pub fn clear_coverage_data(&mut self)

Clear the gathered policy coverage data.

source

pub fn set_gather_prints(&mut self, b: bool)

Gather output from print statements instead of emiting to stderr.

See Engine::take_prints.

source

pub fn take_prints(&mut self) -> Result<Vec<String>>

Take the gathered output of print statements.

let mut engine = Engine::new();

// Print to stderr.
engine.eval_query("print(\"Hello\")".to_string(), false)?;

// Configure gathering print statements.
engine.set_gather_prints(true);

// Execute query.
engine.eval_query("print(\"Hello\")".to_string(), false)?;

// Take and clear prints.
let prints = engine.take_prints()?;
assert_eq!(prints.len(), 1);
assert!(prints[0].contains("Hello"));

for p in prints {
  println!("{p}");
}

Trait Implementations§

source§

impl Clone for Engine

source§

fn clone(&self) -> Engine

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Engine

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Engine

Create a default engine.

source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Engine

§

impl !RefUnwindSafe for Engine

§

impl Send for Engine

§

impl Sync for Engine

§

impl Unpin for Engine

§

impl !UnwindSafe for Engine

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

source§

fn vzip(self) -> V