pub struct Engine { /* private fields */ }
Expand description
The Rego evaluation engine.
Implementations§
Source§impl Engine
impl Engine
Sourcepub fn set_rego_v0(&mut self, rego_v0: bool)
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())?;
Sourcepub fn add_policy(&mut self, path: String, rego: String) -> Result<String>
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");
Sourcepub fn get_packages(&self) -> Result<Vec<String>>
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"]);
Sourcepub fn get_policies(&self) -> Result<Vec<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");
Sourcepub fn get_policies_as_json(&self) -> Result<String>
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");
Sourcepub fn set_input(&mut self, input: Value)
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);
pub fn set_input_json(&mut self, input_json: &str) -> Result<()>
Sourcepub fn clear_data(&mut self)
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());
Sourcepub fn add_data(&mut self, data: Value) -> Result<()>
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}"#)?
);
Sourcepub fn get_data(&self) -> Value
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));
pub fn add_data_json(&mut self, data_json: &str) -> Result<()>
Sourcepub fn set_strict_builtin_errors(&mut self, b: bool)
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.
Sourcepub fn compile_with_entrypoint(
&mut self,
rule: &Rc<str>,
) -> Result<CompiledPolicy>
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:
- Policy modules added via
Engine::add_policy
- Data added via
Engine::add_data
(optional)
§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 (requiresazure_policy
feature)
§See Also
- [
Engine::compile_for_target
] for target-aware compilation crate::compile_policy_with_entrypoint
for a higher-level convenience function
Sourcepub fn eval_rule(&mut self, rule: String) -> Result<Value>
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());
Sourcepub fn eval_query(
&mut self,
query: String,
enable_tracing: bool,
) -> Result<QueryResults>
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));
Sourcepub fn eval_bool_query(
&mut self,
query: String,
enable_tracing: bool,
) -> Result<bool>
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());
Sourcepub fn eval_allow_query(&mut self, query: String, enable_tracing: bool) -> bool
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);
Sourcepub fn eval_deny_query(&mut self, query: String, enable_tracing: bool) -> bool
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);
Sourcepub fn add_extension(
&mut self,
path: String,
nargs: u8,
extension: Box<dyn Extension>,
) -> Result<()>
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
: TheExtension
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 ¶ms[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());
Sourcepub fn set_gather_prints(&mut self, b: bool)
pub fn set_gather_prints(&mut self, b: bool)
Gather output from print statements instead of emiting to stderr.
See Engine::take_prints
.
Sourcepub fn take_prints(&mut self) -> Result<Vec<String>>
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}");
}