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 add_policy_from_file<P: AsRef<Path>>(
&mut self,
path: P,
) -> Result<String>
Available on crate feature std
only.
pub fn add_policy_from_file<P: AsRef<Path>>( &mut self, path: P, ) -> Result<String>
std
only.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. Returns the Rego package name declared in the policy.
path
: Path to the policy file (.rego).
let mut engine = Engine::new();
// framework.rego does not conform to v1.
engine.set_rego_v0(true);
let package = engine.add_policy_from_file("tests/aci/framework.rego")?;
assert_eq!(package, "data.framework");
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_for_target(&mut self) -> Result<CompiledPolicy>
Available on crate feature azure_policy
only.
pub fn compile_for_target(&mut self) -> Result<CompiledPolicy>
azure_policy
only.Compiles a target-aware policy from the current engine state.
This method creates a compiled policy that can work with Azure Policy targets,
enabling resource type inference and target-specific evaluation. The compiled
policy will automatically detect and handle __target__
declarations in the
loaded modules.
The engine must have been prepared with:
- Policy modules added via
Engine::add_policy
- Data added via
Engine::add_data
(optional)
§Returns
Returns a CompiledPolicy
that can be used for efficient policy evaluation
with target support, including resource type inference capabilities.
§Examples
§Basic Target-Aware Compilation
use regorus::*;
let mut engine = Engine::new();
engine.add_data(Value::from_json_str(r#"{"allowed_sizes": ["small", "medium"]}"#)?)?;
engine.add_policy("policy.rego".to_string(), r#"
package policy.test
import rego.v1
__target__ := "target.tests.sample_test_target"
default allow := false
allow if {
input.type == "vm"
input.size in data.allowed_sizes
}
"#.to_string())?;
let compiled = engine.compile_for_target()?;
let result = compiled.eval_with_input(Value::from_json_str(r#"{"type": "vm", "size": "small"}"#)?)?;
§Target Registration and Usage
use regorus::*;
use regorus::registry::targets;
use regorus::target::Target;
use std::sync::Arc;
// Register a target first
let target_json = r#"
{
"name": "target.example.vm_policy",
"description": "Simple VM validation target",
"version": "1.0.0",
"resource_schema_selector": "type",
"resource_schemas": [
{
"type": "object",
"properties": {
"name": { "type": "string" },
"type": { "const": "vm" },
"size": { "enum": ["small", "medium", "large"] }
},
"required": ["name", "type", "size"]
}
],
"effects": {
"allow": { "type": "boolean" },
"deny": { "type": "boolean" }
}
}
"#;
let target = Target::from_json_str(target_json)?;
targets::register(Arc::new(target))?;
// Use the target in a policy
let mut engine = Engine::new();
engine.add_data(Value::from_json_str(r#"{"allowed_locations": ["us-east"]}"#)?)?;
engine.add_policy("vm_policy.rego".to_string(), r#"
package vm.validation
import rego.v1
__target__ := "target.example.vm_policy"
default allow := false
allow if {
input.type == "vm"
input.size in ["small", "medium"]
}
"#.to_string())?;
let compiled = engine.compile_for_target()?;
let result = compiled.eval_with_input(Value::from_json_str(r#"
{
"name": "test-vm",
"type": "vm",
"size": "small"
}"#)?)?;
assert_eq!(result, Value::from(true));
§Notes
- This method is only available when the
azure_policy
feature is enabled - Automatically enables print gathering for debugging purposes
- Requires that at least one module contains a
__target__
declaration - The target referenced must be registered in the target registry
§See Also
Engine::compile_with_entrypoint
for explicit rule-based compilationcrate::compile_policy_for_target
for a higher-level convenience function
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 usingEngine::compile_for_target
instead (requiresazure_policy
feature)
§See Also
Engine::compile_for_target
for target-aware compilationcrate::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 get_coverage_report(&self) -> Result<Report>
Available on crate feature coverage
only.
pub fn get_coverage_report(&self) -> Result<Report>
coverage
only.Get the coverage report.
let mut engine = Engine::new();
engine.add_policy(
"policy.rego".to_string(),
r#"
package test # Line 2
x = y if { # 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
].
Sourcepub fn set_enable_coverage(&mut self, enable: bool)
Available on crate feature coverage
only.
pub fn set_enable_coverage(&mut self, enable: bool)
coverage
only.Enable/disable policy coverage.
If enable
is different from the current value, then any existing coverage
information will be cleared.
Sourcepub fn clear_coverage_data(&mut self)
Available on crate feature coverage
only.
pub fn clear_coverage_data(&mut self)
coverage
only.Clear the gathered policy coverage data.
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}");
}
Sourcepub fn get_ast_as_json(&self) -> Result<String>
Available on crate feature ast
only.
pub fn get_ast_as_json(&self) -> Result<String>
ast
only.Get the policies and corresponding AST.
engine.add_policy("test.rego".to_string(), "package test\n x := 1".to_string())?;
let ast = engine.get_ast_as_json()?;
let value = Value::from_json_str(&ast)?;
assert_eq!(value[0]["ast"]["package"]["refr"]["Var"][1].as_string()?.as_ref(), "test");
Sourcepub fn get_policy_package_names(
&self,
) -> Result<Vec<PolicyPackageNameDefinition>>
Available on crate feature azure_policy
only.
pub fn get_policy_package_names( &self, ) -> Result<Vec<PolicyPackageNameDefinition>>
azure_policy
only.Get the package names of each policy added to the engine.
engine.add_policy("test.rego".to_string(), "package test\n x := 1".to_string())?;
engine.add_policy("test2.rego".to_string(), "package test.multi.segment\n x := 1".to_string())?;
let package_names = engine.get_policy_package_names()?;
assert_eq!("test", package_names[0].package_name);
assert_eq!("test.multi.segment", package_names[1].package_name);
Sourcepub fn get_policy_parameters(&self) -> Result<Vec<PolicyParameters>>
Available on crate feature azure_policy
only.
pub fn get_policy_parameters(&self) -> Result<Vec<PolicyParameters>>
azure_policy
only.Get the parameters defined in each policy.
engine.add_policy("test.rego".to_string(), "package test default parameters.a = 5 parameters.b = 10\n x := 1".to_string())?;
let parameters = engine.get_policy_parameters()?;
assert_eq!("a", parameters[0].parameters[0].name);
assert_eq!("b", parameters[0].modifiers[0].name);