Struct 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 set_rego_v0(&mut self, rego_v0: bool)

Enable rego v0.

Note that regorus now defaults to v1.

let mut engine = Engine::new();

// Enable v0 for old style policies.
engine.set_rego_v0(true);

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

   allow { # v0 syntax does not require if keyword
      1 < 2
   }
   "#.to_string())?;
Source

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

Add a policy.

The policy file will be parsed and converted to AST representation. Multiple policy files may be added to the engine. Returns the Rego package name declared in the policy.

  • path: A filename to be associated with the policy.
  • rego: The rego policy code.
let mut engine = Engine::new();

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

assert_eq!(package, "data.test");
Source

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

Get the list of packages defined by loaded policies.

let mut engine = Engine::new();
// framework.rego does not conform to v1.
engine.set_rego_v0(true);

let _ = engine.add_policy_from_file("tests/aci/framework.rego")?;

// Package names can be different from file names.
let _ = engine.add_policy("policy.rego".into(), "package hello.world".into())?;

assert_eq!(engine.get_packages()?, vec!["data.framework", "data.hello.world"]);
Source

pub fn get_policies(&self) -> Result<Vec<Source>>

Get the list of policy files.


let pkg = engine.add_policy("hello.rego".to_string(), "package test".to_string())?;
assert_eq!(pkg, "data.test");

let policies = engine.get_policies()?;

assert_eq!(policies[0].get_path(), "hello.rego");
assert_eq!(policies[0].get_contents(), "package test");
Source

pub fn get_policies_as_json(&self) -> Result<String>

Get the list of policy files as a JSON object.


let pkg = engine.add_policy("hello.rego".to_string(), "package test".to_string())?;
assert_eq!(pkg, "data.test");

let policies = engine.get_policies_as_json()?;

let v = Value::from_json_str(&policies)?;
assert_eq!(v[0]["path"].as_string()?.as_ref(), "hello.rego");
assert_eq!(v[0]["contents"].as_string()?.as_ref(), "package test");
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 get_data(&self) -> Value

Get the data document.

The returned value is the data document that has been constructed using one or more calls to [Engine::pre]. The values of policy rules are not included in the returned document.

let mut engine = Engine::new();

// If not set, data document is empty.
assert_eq!(engine.get_data(), Value::new_object());

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

let data = engine.get_data();
assert_eq!(data["x"], Value::from(1));
assert_eq!(data["y"], Value::new_object());
assert_eq!(data["z"], Value::from(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 compile_with_entrypoint( &mut self, rule: &Rc<str>, ) -> Result<CompiledPolicy>

Compiles a policy with a specific entry point rule.

This method creates a compiled policy that evaluates a specific rule as the entry point. Unlike [Engine::compile_for_target], this method requires you to explicitly specify which rule should be evaluated and does not automatically handle target-specific features.

The engine must have been prepared with:

§Arguments
  • rule - The specific rule path to evaluate (e.g., “data.policy.allow”)
§Returns

Returns a CompiledPolicy that can be used for efficient policy evaluation focused on the specified entry point rule.

§Examples
§Basic Usage
use regorus::*;
use std::rc::Rc;

let mut engine = Engine::new();
engine.add_data(Value::from_json_str(r#"{"allowed_users": ["alice", "bob"]}"#)?)?;
engine.add_policy("authz.rego".to_string(), r#"
    package authz
    import rego.v1
     
    default allow := false
    allow if {
        input.user in data.allowed_users
        input.action == "read"
    }
     
    deny if {
        input.user == "guest"
    }
"#.to_string())?;

let compiled = engine.compile_with_entrypoint(&"data.authz.allow".into())?;
let result = compiled.eval_with_input(Value::from_json_str(r#"{"user": "alice", "action": "read"}"#)?)?;
assert_eq!(result, Value::from(true));
§Multi-Module Policy
use regorus::*;
use std::rc::Rc;

let mut engine = Engine::new();
engine.add_data(Value::from_json_str(r#"{"departments": {"engineering": ["alice"], "hr": ["bob"]}}"#)?)?;

engine.add_policy("users.rego".to_string(), r#"
    package users
    import rego.v1
     
    user_department(user) := dept if {
        dept := [d | data.departments[d][_] == user][0]
    }
"#.to_string())?;

engine.add_policy("permissions.rego".to_string(), r#"
    package permissions
    import rego.v1
    import data.users
     
    default allow := false
    allow if {
        users.user_department(input.user) == "engineering"
        input.resource.type == "code"
    }
     
    allow if {
        users.user_department(input.user) == "hr"
        input.resource.type == "personnel_data"
    }
"#.to_string())?;

let compiled = engine.compile_with_entrypoint(&"data.permissions.allow".into())?;

// Test engineering access to code
let result = compiled.eval_with_input(Value::from_json_str(r#"
{
  "user": "alice",
  "resource": {"type": "code", "name": "main.rs"}
}"#)?)?;
assert_eq!(result, Value::from(true));
§Entry Point Rule Format

The rule parameter should follow the Rego rule path format:

  • "data.package.rule" - For rules in a specific package
  • "data.package.subpackage.rule" - For nested packages
  • "allow" - For rules in the default package (though this is not recommended)
§Notes
  • Automatically enables print gathering for debugging purposes
  • If you need target-aware compilation with automatic __target__ handling, consider using [Engine::compile_for_target] instead (requires azure_policy feature)
§See Also
Source

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

Evaluate specified rule(s).

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.set_rego_v0(true);
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 if {
      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 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 duplicate 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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> ToOwned for T
where T: Clone,

Source§

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>,

Source§

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>,

Source§

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

Source§

impl<T> ErasedDestructor for T
where T: 'static,