regorus/engine.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::ast::*;
5use crate::compiled_policy::CompiledPolicy;
6use crate::interpreter::*;
7use crate::lexer::*;
8use crate::parser::*;
9use crate::scheduler::*;
10use crate::utils::gather_functions;
11use crate::value::*;
12use crate::*;
13use crate::{Extension, QueryResults};
14
15use anyhow::{bail, Result};
16
17/// The Rego evaluation engine.
18///
19#[derive(Debug, Clone)]
20pub struct Engine {
21 modules: Rc<Vec<Ref<Module>>>,
22 interpreter: Interpreter,
23 prepared: bool,
24 rego_v1: bool,
25}
26
27#[cfg(feature = "azure_policy")]
28#[derive(Debug, Clone, Serialize)]
29pub struct PolicyPackageNameDefinition {
30 pub source_file: String,
31 pub package_name: String,
32}
33
34#[cfg(feature = "azure_policy")]
35#[derive(Debug, Clone, Serialize)]
36pub struct PolicyParameter {
37 pub name: String,
38 pub modifiable: bool,
39 pub required: bool,
40}
41
42#[cfg(feature = "azure_policy")]
43#[derive(Debug, Clone, Serialize)]
44pub struct PolicyModifier {
45 pub name: String,
46}
47
48#[cfg(feature = "azure_policy")]
49#[derive(Debug, Clone, Serialize)]
50pub struct PolicyParameters {
51 pub source_file: String,
52 pub parameters: Vec<PolicyParameter>,
53 pub modifiers: Vec<PolicyModifier>,
54}
55
56/// Create a default engine.
57impl Default for Engine {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63impl Engine {
64 /// Create an instance of [Engine].
65 pub fn new() -> Self {
66 Self {
67 modules: Rc::new(vec![]),
68 interpreter: Interpreter::new(),
69 prepared: false,
70 rego_v1: true,
71 }
72 }
73
74 /// Enable rego v0.
75 ///
76 /// Note that regorus now defaults to v1.
77 /// ```
78 /// # use regorus::*;
79 /// # fn main() -> anyhow::Result<()> {
80 /// let mut engine = Engine::new();
81 ///
82 /// // Enable v0 for old style policies.
83 /// engine.set_rego_v0(true);
84 ///
85 /// engine.add_policy(
86 /// "test.rego".to_string(),
87 /// r#"
88 /// package test
89 ///
90 /// allow { # v0 syntax does not require if keyword
91 /// 1 < 2
92 /// }
93 /// "#.to_string())?;
94 ///
95 /// # Ok(())
96 /// # }
97 /// ```
98 ///
99 pub fn set_rego_v0(&mut self, rego_v0: bool) {
100 self.rego_v1 = !rego_v0;
101 }
102
103 /// Add a policy.
104 ///
105 /// The policy file will be parsed and converted to AST representation.
106 /// Multiple policy files may be added to the engine.
107 /// Returns the Rego package name declared in the policy.
108 ///
109 /// * `path`: A filename to be associated with the policy.
110 /// * `rego`: The rego policy code.
111 ///
112 /// ```
113 /// # use regorus::*;
114 /// # fn main() -> anyhow::Result<()> {
115 /// let mut engine = Engine::new();
116 ///
117 /// let package = engine.add_policy(
118 /// "test.rego".to_string(),
119 /// r#"
120 /// package test
121 /// allow = input.user == "root"
122 /// "#.to_string())?;
123 ///
124 /// assert_eq!(package, "data.test");
125 /// # Ok(())
126 /// # }
127 /// ```
128 ///
129 pub fn add_policy(&mut self, path: String, rego: String) -> Result<String> {
130 let source = Source::from_contents(path, rego)?;
131 let mut parser = self.make_parser(&source)?;
132 let module = Ref::new(parser.parse()?);
133 Rc::make_mut(&mut self.modules).push(module.clone());
134 // if policies change, interpreter needs to be prepared again
135 self.prepared = false;
136 Interpreter::get_path_string(&module.package.refr, Some("data"))
137 }
138
139 /// Add a policy from a given file.
140 ///
141 /// The policy file will be parsed and converted to AST representation.
142 /// Multiple policy files may be added to the engine.
143 /// Returns the Rego package name declared in the policy.
144 ///
145 /// * `path`: Path to the policy file (.rego).
146 ///
147 /// ```
148 /// # use regorus::*;
149 /// # fn main() -> anyhow::Result<()> {
150 /// let mut engine = Engine::new();
151 /// // framework.rego does not conform to v1.
152 /// engine.set_rego_v0(true);
153 ///
154 /// let package = engine.add_policy_from_file("tests/aci/framework.rego")?;
155 ///
156 /// assert_eq!(package, "data.framework");
157 /// # Ok(())
158 /// # }
159 /// ```
160 #[cfg(feature = "std")]
161 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
162 pub fn add_policy_from_file<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<String> {
163 let source = Source::from_file(path)?;
164 let mut parser = self.make_parser(&source)?;
165 let module = Ref::new(parser.parse()?);
166 Rc::make_mut(&mut self.modules).push(module.clone());
167 // if policies change, interpreter needs to be prepared again
168 self.prepared = false;
169 Interpreter::get_path_string(&module.package.refr, Some("data"))
170 }
171
172 /// Get the list of packages defined by loaded policies.
173 ///
174 /// ```
175 /// # use regorus::*;
176 /// # fn main() -> anyhow::Result<()> {
177 /// let mut engine = Engine::new();
178 /// // framework.rego does not conform to v1.
179 /// engine.set_rego_v0(true);
180 ///
181 /// let _ = engine.add_policy_from_file("tests/aci/framework.rego")?;
182 ///
183 /// // Package names can be different from file names.
184 /// let _ = engine.add_policy("policy.rego".into(), "package hello.world".into())?;
185 ///
186 /// assert_eq!(engine.get_packages()?, vec!["data.framework", "data.hello.world"]);
187 /// # Ok(())
188 /// # }
189 /// ```
190 pub fn get_packages(&self) -> Result<Vec<String>> {
191 self.modules
192 .iter()
193 .map(|m| Interpreter::get_path_string(&m.package.refr, Some("data")))
194 .collect()
195 }
196
197 /// Get the list of policy files.
198 /// ```
199 /// # use regorus::*;
200 /// # fn main() -> anyhow::Result<()> {
201 /// # let mut engine = Engine::new();
202 ///
203 /// let pkg = engine.add_policy("hello.rego".to_string(), "package test".to_string())?;
204 /// assert_eq!(pkg, "data.test");
205 ///
206 /// let policies = engine.get_policies()?;
207 ///
208 /// assert_eq!(policies[0].get_path(), "hello.rego");
209 /// assert_eq!(policies[0].get_contents(), "package test");
210 /// # Ok(())
211 /// # }
212 /// ```
213 pub fn get_policies(&self) -> Result<Vec<Source>> {
214 Ok(self
215 .modules
216 .iter()
217 .map(|m| m.package.refr.span().source.clone())
218 .collect())
219 }
220
221 /// Get the list of policy files as a JSON object.
222 /// ```
223 /// # use regorus::*;
224 /// # fn main() -> anyhow::Result<()> {
225 /// # let mut engine = Engine::new();
226 ///
227 /// let pkg = engine.add_policy("hello.rego".to_string(), "package test".to_string())?;
228 /// assert_eq!(pkg, "data.test");
229 ///
230 /// let policies = engine.get_policies_as_json()?;
231 ///
232 /// let v = Value::from_json_str(&policies)?;
233 /// assert_eq!(v[0]["path"].as_string()?.as_ref(), "hello.rego");
234 /// assert_eq!(v[0]["contents"].as_string()?.as_ref(), "package test");
235 /// # Ok(())
236 /// # }
237 /// ```
238 pub fn get_policies_as_json(&self) -> Result<String> {
239 #[derive(Serialize)]
240 struct Source<'a> {
241 path: &'a String,
242 contents: &'a String,
243 }
244
245 let mut sources = vec![];
246 for m in self.modules.iter() {
247 let source = &m.package.refr.span().source;
248 sources.push(Source {
249 path: source.get_path(),
250 contents: source.get_contents(),
251 });
252 }
253
254 serde_json::to_string_pretty(&sources).map_err(anyhow::Error::msg)
255 }
256
257 /// Set the input document.
258 ///
259 /// * `input`: Input documented. Typically this [Value] is constructed from JSON or YAML.
260 ///
261 /// ```
262 /// # use regorus::*;
263 /// # fn main() -> anyhow::Result<()> {
264 /// let mut engine = Engine::new();
265 ///
266 /// let input = Value::from_json_str(r#"
267 /// {
268 /// "role" : "admin",
269 /// "action": "delete"
270 /// }"#)?;
271 ///
272 /// engine.set_input(input);
273 /// # Ok(())
274 /// # }
275 /// ```
276 pub fn set_input(&mut self, input: Value) {
277 self.interpreter.set_input(input);
278 }
279
280 pub fn set_input_json(&mut self, input_json: &str) -> Result<()> {
281 self.set_input(Value::from_json_str(input_json)?);
282 Ok(())
283 }
284
285 /// Clear the data document.
286 ///
287 /// The data document will be reset to an empty object.
288 ///
289 /// ```
290 /// # use regorus::*;
291 /// # fn main() -> anyhow::Result<()> {
292 /// let mut engine = Engine::new();
293 ///
294 /// engine.clear_data();
295 ///
296 /// // Evaluate data.
297 /// let results = engine.eval_query("data".to_string(), false)?;
298 ///
299 /// // Assert that it is empty object.
300 /// assert_eq!(results.result.len(), 1);
301 /// assert_eq!(results.result[0].expressions.len(), 1);
302 /// assert_eq!(results.result[0].expressions[0].value, Value::new_object());
303 /// # Ok(())
304 /// # }
305 /// ```
306 pub fn clear_data(&mut self) {
307 self.interpreter.set_init_data(Value::new_object());
308 self.prepared = false;
309 }
310
311 /// Add data document.
312 ///
313 /// The specified data document is merged into existing data document.
314 ///
315 /// ```
316 /// # use regorus::*;
317 /// # fn main() -> anyhow::Result<()> {
318 /// let mut engine = Engine::new();
319 ///
320 /// // Only objects can be added.
321 /// assert!(engine.add_data(Value::from_json_str("[]")?).is_err());
322 ///
323 /// // Merge { "x" : 1, "y" : {} }
324 /// assert!(engine.add_data(Value::from_json_str(r#"{ "x" : 1, "y" : {}}"#)?).is_ok());
325 ///
326 /// // Merge { "z" : 2 }
327 /// assert!(engine.add_data(Value::from_json_str(r#"{ "z" : 2 }"#)?).is_ok());
328 ///
329 /// // Merge { "z" : 3 }. Conflict error.
330 /// assert!(engine.add_data(Value::from_json_str(r#"{ "z" : 3 }"#)?).is_err());
331 ///
332 /// assert_eq!(
333 /// engine.eval_query("data".to_string(), false)?.result[0].expressions[0].value,
334 /// Value::from_json_str(r#"{ "x": 1, "y": {}, "z": 2}"#)?
335 /// );
336 /// # Ok(())
337 /// # }
338 /// ```
339 pub fn add_data(&mut self, data: Value) -> Result<()> {
340 if data.as_object().is_err() {
341 bail!("data must be object");
342 }
343 self.prepared = false;
344 self.interpreter.get_init_data_mut().merge(data)
345 }
346
347 /// Get the data document.
348 ///
349 /// The returned value is the data document that has been constructed using
350 /// one or more calls to [`Engine::pre`]. The values of policy rules are
351 /// not included in the returned document.
352 ///
353 ///
354 /// ```
355 /// # use regorus::*;
356 /// # fn main() -> anyhow::Result<()> {
357 /// let mut engine = Engine::new();
358 ///
359 /// // If not set, data document is empty.
360 /// assert_eq!(engine.get_data(), Value::new_object());
361 ///
362 /// // Merge { "x" : 1, "y" : {} }
363 /// assert!(engine.add_data(Value::from_json_str(r#"{ "x" : 1, "y" : {}}"#)?).is_ok());
364 ///
365 /// // Merge { "z" : 2 }
366 /// assert!(engine.add_data(Value::from_json_str(r#"{ "z" : 2 }"#)?).is_ok());
367 ///
368 /// let data = engine.get_data();
369 /// assert_eq!(data["x"], Value::from(1));
370 /// assert_eq!(data["y"], Value::new_object());
371 /// assert_eq!(data["z"], Value::from(2));
372 ///
373 /// # Ok(())
374 /// # }
375 /// ```
376 pub fn get_data(&self) -> Value {
377 self.interpreter.get_init_data().clone()
378 }
379
380 pub fn add_data_json(&mut self, data_json: &str) -> Result<()> {
381 self.add_data(Value::from_json_str(data_json)?)
382 }
383
384 /// Set whether builtins should raise errors strictly or not.
385 ///
386 /// Regorus differs from OPA in that by default builtins will
387 /// raise errors instead of returning Undefined.
388 ///
389 /// ----
390 /// **_NOTE:_** Currently not all builtins honor this flag and will always strictly raise errors.
391 /// ----
392 pub fn set_strict_builtin_errors(&mut self, b: bool) {
393 self.interpreter.set_strict_builtin_errors(b)
394 }
395
396 #[doc(hidden)]
397 pub fn get_modules(&mut self) -> &Vec<Ref<Module>> {
398 &self.modules
399 }
400
401 /// Compiles a target-aware policy from the current engine state.
402 ///
403 /// This method creates a compiled policy that can work with Azure Policy targets,
404 /// enabling resource type inference and target-specific evaluation. The compiled
405 /// policy will automatically detect and handle `__target__` declarations in the
406 /// loaded modules.
407 ///
408 /// The engine must have been prepared with:
409 /// - Policy modules added via [`Engine::add_policy`]
410 /// - Data added via [`Engine::add_data`] (optional)
411 ///
412 /// # Returns
413 ///
414 /// Returns a [`CompiledPolicy`] that can be used for efficient policy evaluation
415 /// with target support, including resource type inference capabilities.
416 ///
417 /// # Examples
418 ///
419 /// ## Basic Target-Aware Compilation
420 ///
421 /// ```no_run
422 /// use regorus::*;
423 ///
424 /// # fn main() -> anyhow::Result<()> {
425 /// let mut engine = Engine::new();
426 /// engine.add_data(Value::from_json_str(r#"{"allowed_sizes": ["small", "medium"]}"#)?)?;
427 /// engine.add_policy("policy.rego".to_string(), r#"
428 /// package policy.test
429 /// import rego.v1
430 /// __target__ := "target.tests.sample_test_target"
431 ///
432 /// default allow := false
433 /// allow if {
434 /// input.type == "vm"
435 /// input.size in data.allowed_sizes
436 /// }
437 /// "#.to_string())?;
438 ///
439 /// let compiled = engine.compile_for_target()?;
440 /// let result = compiled.eval_with_input(Value::from_json_str(r#"{"type": "vm", "size": "small"}"#)?)?;
441 /// # Ok(())
442 /// # }
443 /// ```
444 ///
445 /// ## Target Registration and Usage
446 ///
447 /// ```no_run
448 /// use regorus::*;
449 /// use regorus::registry::targets;
450 /// use regorus::target::Target;
451 /// use std::sync::Arc;
452 ///
453 /// # fn main() -> anyhow::Result<()> {
454 /// // Register a target first
455 /// let target_json = r#"
456 /// {
457 /// "name": "target.example.vm_policy",
458 /// "description": "Simple VM validation target",
459 /// "version": "1.0.0",
460 /// "resource_schema_selector": "type",
461 /// "resource_schemas": [
462 /// {
463 /// "type": "object",
464 /// "properties": {
465 /// "name": { "type": "string" },
466 /// "type": { "const": "vm" },
467 /// "size": { "enum": ["small", "medium", "large"] }
468 /// },
469 /// "required": ["name", "type", "size"]
470 /// }
471 /// ],
472 /// "effects": {
473 /// "allow": { "type": "boolean" },
474 /// "deny": { "type": "boolean" }
475 /// }
476 /// }
477 /// "#;
478 ///
479 /// let target = Target::from_json_str(target_json)?;
480 /// targets::register(Arc::new(target))?;
481 ///
482 /// // Use the target in a policy
483 /// let mut engine = Engine::new();
484 /// engine.add_data(Value::from_json_str(r#"{"allowed_locations": ["us-east"]}"#)?)?;
485 /// engine.add_policy("vm_policy.rego".to_string(), r#"
486 /// package vm.validation
487 /// import rego.v1
488 /// __target__ := "target.example.vm_policy"
489 ///
490 /// default allow := false
491 /// allow if {
492 /// input.type == "vm"
493 /// input.size in ["small", "medium"]
494 /// }
495 /// "#.to_string())?;
496 ///
497 /// let compiled = engine.compile_for_target()?;
498 /// let result = compiled.eval_with_input(Value::from_json_str(r#"
499 /// {
500 /// "name": "test-vm",
501 /// "type": "vm",
502 /// "size": "small"
503 /// }"#)?)?;
504 /// assert_eq!(result, Value::from(true));
505 /// # Ok(())
506 /// # }
507 /// ```
508 ///
509 /// # Notes
510 ///
511 /// - This method is only available when the `azure_policy` feature is enabled
512 /// - Automatically enables print gathering for debugging purposes
513 /// - Requires that at least one module contains a `__target__` declaration
514 /// - The target referenced must be registered in the target registry
515 ///
516 /// # See Also
517 ///
518 /// - [`Engine::compile_with_entrypoint`] for explicit rule-based compilation
519 /// - [`crate::compile_policy_for_target`] for a higher-level convenience function
520 #[cfg(feature = "azure_policy")]
521 #[cfg_attr(docsrs, doc(cfg(feature = "azure_policy")))]
522 pub fn compile_for_target(&mut self) -> Result<CompiledPolicy> {
523 self.prepare_for_eval(false, true)?;
524 self.interpreter.clean_internal_evaluation_state();
525 self.interpreter.compile(None).map(CompiledPolicy::new)
526 }
527
528 /// Compiles a policy with a specific entry point rule.
529 ///
530 /// This method creates a compiled policy that evaluates a specific rule as the entry point.
531 /// Unlike [`Engine::compile_for_target`], this method requires you to explicitly specify which
532 /// rule should be evaluated and does not automatically handle target-specific features.
533 ///
534 /// The engine must have been prepared with:
535 /// - Policy modules added via [`Engine::add_policy`]
536 /// - Data added via [`Engine::add_data`] (optional)
537 ///
538 /// # Arguments
539 ///
540 /// * `rule` - The specific rule path to evaluate (e.g., "data.policy.allow")
541 ///
542 /// # Returns
543 ///
544 /// Returns a [`CompiledPolicy`] that can be used for efficient policy evaluation
545 /// focused on the specified entry point rule.
546 ///
547 /// # Examples
548 ///
549 /// ## Basic Usage
550 ///
551 /// ```no_run
552 /// use regorus::*;
553 /// use std::rc::Rc;
554 ///
555 /// # fn main() -> anyhow::Result<()> {
556 /// let mut engine = Engine::new();
557 /// engine.add_data(Value::from_json_str(r#"{"allowed_users": ["alice", "bob"]}"#)?)?;
558 /// engine.add_policy("authz.rego".to_string(), r#"
559 /// package authz
560 /// import rego.v1
561 ///
562 /// default allow := false
563 /// allow if {
564 /// input.user in data.allowed_users
565 /// input.action == "read"
566 /// }
567 ///
568 /// deny if {
569 /// input.user == "guest"
570 /// }
571 /// "#.to_string())?;
572 ///
573 /// let compiled = engine.compile_with_entrypoint(&"data.authz.allow".into())?;
574 /// let result = compiled.eval_with_input(Value::from_json_str(r#"{"user": "alice", "action": "read"}"#)?)?;
575 /// assert_eq!(result, Value::from(true));
576 /// # Ok(())
577 /// # }
578 /// ```
579 ///
580 /// ## Multi-Module Policy
581 ///
582 /// ```no_run
583 /// use regorus::*;
584 /// use std::rc::Rc;
585 ///
586 /// # fn main() -> anyhow::Result<()> {
587 /// let mut engine = Engine::new();
588 /// engine.add_data(Value::from_json_str(r#"{"departments": {"engineering": ["alice"], "hr": ["bob"]}}"#)?)?;
589 ///
590 /// engine.add_policy("users.rego".to_string(), r#"
591 /// package users
592 /// import rego.v1
593 ///
594 /// user_department(user) := dept if {
595 /// dept := [d | data.departments[d][_] == user][0]
596 /// }
597 /// "#.to_string())?;
598 ///
599 /// engine.add_policy("permissions.rego".to_string(), r#"
600 /// package permissions
601 /// import rego.v1
602 /// import data.users
603 ///
604 /// default allow := false
605 /// allow if {
606 /// users.user_department(input.user) == "engineering"
607 /// input.resource.type == "code"
608 /// }
609 ///
610 /// allow if {
611 /// users.user_department(input.user) == "hr"
612 /// input.resource.type == "personnel_data"
613 /// }
614 /// "#.to_string())?;
615 ///
616 /// let compiled = engine.compile_with_entrypoint(&"data.permissions.allow".into())?;
617 ///
618 /// // Test engineering access to code
619 /// let result = compiled.eval_with_input(Value::from_json_str(r#"
620 /// {
621 /// "user": "alice",
622 /// "resource": {"type": "code", "name": "main.rs"}
623 /// }"#)?)?;
624 /// assert_eq!(result, Value::from(true));
625 /// # Ok(())
626 /// # }
627 /// ```
628 ///
629 /// # Entry Point Rule Format
630 ///
631 /// The `rule` parameter should follow the Rego rule path format:
632 /// - `"data.package.rule"` - For rules in a specific package
633 /// - `"data.package.subpackage.rule"` - For nested packages
634 /// - `"allow"` - For rules in the default package (though this is not recommended)
635 ///
636 /// # Notes
637 ///
638 /// - Automatically enables print gathering for debugging purposes
639 /// - If you need target-aware compilation with automatic `__target__` handling,
640 /// consider using [`Engine::compile_for_target`] instead (requires `azure_policy` feature)
641 ///
642 /// # See Also
643 ///
644 /// - [`Engine::compile_for_target`] for target-aware compilation
645 /// - [`crate::compile_policy_with_entrypoint`] for a higher-level convenience function
646 pub fn compile_with_entrypoint(&mut self, rule: &Rc<str>) -> Result<CompiledPolicy> {
647 self.prepare_for_eval(false, false)?;
648 self.interpreter.clean_internal_evaluation_state();
649 self.interpreter
650 .compile(Some(rule.clone()))
651 .map(CompiledPolicy::new)
652 }
653
654 /// Evaluate specified rule(s).
655 ///
656 /// [`Engine::eval_rule`] is often faster than [`Engine::eval_query`] and should be preferred if
657 /// OPA style [`QueryResults`] are not needed.
658 ///
659 /// ```
660 /// # use regorus::*;
661 /// # fn main() -> anyhow::Result<()> {
662 /// let mut engine = Engine::new();
663 ///
664 /// // Add policy
665 /// engine.add_policy(
666 /// "policy.rego".to_string(),
667 /// r#"
668 /// package example
669 /// import rego.v1
670 ///
671 /// x = [1, 2]
672 ///
673 /// y := 5 if input.a > 2
674 /// "#.to_string())?;
675 ///
676 /// // Evaluate rule.
677 /// let v = engine.eval_rule("data.example.x".to_string())?;
678 /// assert_eq!(v, Value::from(vec![Value::from(1), Value::from(2)]));
679 ///
680 /// // y evaluates to undefined.
681 /// let v = engine.eval_rule("data.example.y".to_string())?;
682 /// assert_eq!(v, Value::Undefined);
683 ///
684 /// // Evaluating a non-existent rule is an error.
685 /// let r = engine.eval_rule("data.exaample.x".to_string());
686 /// assert!(r.is_err());
687 ///
688 /// // Path must be valid rule paths.
689 /// assert!( engine.eval_rule("data".to_string()).is_err());
690 /// assert!( engine.eval_rule("data.example".to_string()).is_err());
691 /// # Ok(())
692 /// # }
693 /// ```
694 pub fn eval_rule(&mut self, rule: String) -> Result<Value> {
695 self.prepare_for_eval(false, false)?;
696 self.interpreter.clean_internal_evaluation_state();
697 self.interpreter.eval_rule_in_path(rule)
698 }
699
700 /// Evaluate a Rego query.
701 ///
702 /// ```
703 /// # use regorus::*;
704 /// # fn main() -> anyhow::Result<()> {
705 /// let mut engine = Engine::new();
706 ///
707 /// // Add policies
708 /// engine.set_rego_v0(true);
709 /// engine.add_policy_from_file("tests/aci/framework.rego")?;
710 /// engine.add_policy_from_file("tests/aci/api.rego")?;
711 /// engine.add_policy_from_file("tests/aci/policy.rego")?;
712 ///
713 /// // Add data document (if any).
714 /// // If multiple data documents can be added, they will be merged together.
715 /// engine.add_data(Value::from_json_file("tests/aci/data.json")?)?;
716 ///
717 /// // At this point the policies and data have been loaded.
718 /// // Either the same engine can be used to make multiple queries or the engine
719 /// // can be cloned to avoid having the reload the policies and data.
720 /// let _clone = engine.clone();
721 ///
722 /// // Evaluate a query.
723 /// // Load input and make query.
724 /// engine.set_input(Value::new_object());
725 /// let results = engine.eval_query("data.framework.mount_overlay.allowed".to_string(), false)?;
726 /// assert_eq!(results.result[0].expressions[0].value, Value::from(false));
727 ///
728 /// // Evaluate query with different inputs.
729 /// engine.set_input(Value::from_json_file("tests/aci/input.json")?);
730 /// let results = engine.eval_query("data.framework.mount_overlay.allowed".to_string(), false)?;
731 /// assert_eq!(results.result[0].expressions[0].value, Value::from(true));
732 /// # Ok(())
733 /// # }
734 /// ```
735 pub fn eval_query(&mut self, query: String, enable_tracing: bool) -> Result<QueryResults> {
736 self.prepare_for_eval(enable_tracing, false)?;
737 self.interpreter.clean_internal_evaluation_state();
738
739 self.interpreter.create_rule_prefixes()?;
740 let query_module = {
741 let source = Source::from_contents(
742 "<query_module.rego>".to_owned(),
743 "package __internal_query_module".to_owned(),
744 )?;
745 Ref::new(Parser::new(&source)?.parse()?)
746 };
747
748 // Parse the query.
749 let query_source = Source::from_contents("<query.rego>".to_string(), query)?;
750 let mut parser = self.make_parser(&query_source)?;
751 let query_node = parser.parse_user_query()?;
752 if query_node.span.text() == "data" {
753 self.eval_modules(enable_tracing)?;
754 }
755 let query_schedule = Analyzer::new().analyze_query_snippet(&self.modules, &query_node)?;
756 self.interpreter.eval_user_query(
757 &query_module,
758 &query_node,
759 &query_schedule,
760 enable_tracing,
761 )
762 }
763
764 /// Evaluate a Rego query that produces a boolean value.
765 ///
766 ///
767 /// This function should be preferred over [`Engine::eval_query`] if just a `true`/`false`
768 /// value is desired instead of [`QueryResults`].
769 ///
770 /// ```
771 /// # use regorus::*;
772 /// # fn main() -> anyhow::Result<()> {
773 /// # let mut engine = Engine::new();
774 ///
775 /// let enable_tracing = false;
776 /// assert_eq!(engine.eval_bool_query("1 > 2".to_string(), enable_tracing)?, false);
777 /// assert_eq!(engine.eval_bool_query("1 < 2".to_string(), enable_tracing)?, true);
778 ///
779 /// // Non boolean queries will raise an error.
780 /// assert!(engine.eval_bool_query("1+1".to_string(), enable_tracing).is_err());
781 ///
782 /// // Queries producing multiple values will raise an error.
783 /// assert!(engine.eval_bool_query("true; true".to_string(), enable_tracing).is_err());
784 ///
785 /// // Queries producing no values will raise an error.
786 /// assert!(engine.eval_bool_query("true; false; true".to_string(), enable_tracing).is_err());
787 /// # Ok(())
788 /// # }
789 /// ```
790 pub fn eval_bool_query(&mut self, query: String, enable_tracing: bool) -> Result<bool> {
791 let results = self.eval_query(query, enable_tracing)?;
792 match results.result.len() {
793 0 => bail!("query did not produce any values"),
794 1 if results.result[0].expressions.len() == 1 => {
795 results.result[0].expressions[0].value.as_bool().copied()
796 }
797 _ => bail!("query produced more than one value"),
798 }
799 }
800
801 /// Evaluate an `allow` query.
802 ///
803 /// This is a wrapper over [`Engine::eval_bool_query`] that returns true only if the
804 /// boolean query succeed and produced a `true` value.
805 ///
806 /// ```
807 /// # use regorus::*;
808 /// # fn main() -> anyhow::Result<()> {
809 /// # let mut engine = Engine::new();
810 ///
811 /// let enable_tracing = false;
812 /// assert_eq!(engine.eval_allow_query("1 > 2".to_string(), enable_tracing), false);
813 /// assert_eq!(engine.eval_allow_query("1 < 2".to_string(), enable_tracing), true);
814 ///
815 /// assert_eq!(engine.eval_allow_query("1+1".to_string(), enable_tracing), false);
816 /// assert_eq!(engine.eval_allow_query("true; true".to_string(), enable_tracing), false);
817 /// assert_eq!(engine.eval_allow_query("true; false; true".to_string(), enable_tracing), false);
818 /// # Ok(())
819 /// # }
820 pub fn eval_allow_query(&mut self, query: String, enable_tracing: bool) -> bool {
821 matches!(self.eval_bool_query(query, enable_tracing), Ok(true))
822 }
823
824 /// Evaluate a `deny` query.
825 ///
826 /// This is a wrapper over [`Engine::eval_bool_query`] that returns false only if the
827 /// boolean query succeed and produced a `false` value.
828 /// ```
829 /// # use regorus::*;
830 /// # fn main() -> anyhow::Result<()> {
831 /// # let mut engine = Engine::new();
832 ///
833 /// let enable_tracing = false;
834 /// assert_eq!(engine.eval_deny_query("1 > 2".to_string(), enable_tracing), false);
835 /// assert_eq!(engine.eval_deny_query("1 < 2".to_string(), enable_tracing), true);
836 ///
837 /// assert_eq!(engine.eval_deny_query("1+1".to_string(), enable_tracing), true);
838 /// assert_eq!(engine.eval_deny_query("true; true".to_string(), enable_tracing), true);
839 /// assert_eq!(engine.eval_deny_query("true; false; true".to_string(), enable_tracing), true);
840 /// # Ok(())
841 /// # }
842 pub fn eval_deny_query(&mut self, query: String, enable_tracing: bool) -> bool {
843 !matches!(self.eval_bool_query(query, enable_tracing), Ok(false))
844 }
845
846 #[doc(hidden)]
847 /// Evaluate the given query and all the rules in the supplied policies.
848 ///
849 /// This is mainly used for testing Regorus itself.
850 pub fn eval_query_and_all_rules(
851 &mut self,
852 query: String,
853 enable_tracing: bool,
854 ) -> Result<QueryResults> {
855 self.eval_modules(enable_tracing)?;
856
857 let query_module = {
858 let source = Source::from_contents(
859 "<query_module.rego>".to_owned(),
860 "package __internal_query_module".to_owned(),
861 )?;
862 Ref::new(Parser::new(&source)?.parse()?)
863 };
864
865 // Parse the query.
866 let query_source = Source::from_contents("<query.rego>".to_string(), query)?;
867 let mut parser = self.make_parser(&query_source)?;
868 let query_node = parser.parse_user_query()?;
869 let query_schedule = Analyzer::new().analyze_query_snippet(&self.modules, &query_node)?;
870 self.interpreter.eval_user_query(
871 &query_module,
872 &query_node,
873 &query_schedule,
874 enable_tracing,
875 )
876 }
877
878 #[doc(hidden)]
879 fn prepare_for_eval(&mut self, enable_tracing: bool, for_target: bool) -> Result<()> {
880 self.interpreter.set_traces(enable_tracing);
881
882 // if the data/policies have changed or the interpreter has never been prepared
883 if !self.prepared {
884 // Analyze the modules and determine how statements must be scheduled.
885 let analyzer = Analyzer::new();
886 let schedule = analyzer.analyze(&self.modules)?;
887
888 self.interpreter.set_schedule(Some(schedule));
889 self.interpreter.set_modules(self.modules.clone());
890
891 self.interpreter.clear_builtins_cache();
892 // clean_internal_evaluation_state will set data to an efficient clont of use supplied init_data
893 // Initialize the with-document with initial data values.
894 // with-modifiers will be applied to this document.
895 self.interpreter.init_with_document()?;
896
897 self.interpreter
898 .set_functions(gather_functions(&self.modules)?);
899 self.interpreter.gather_rules()?;
900 self.interpreter.process_imports()?;
901
902 #[cfg(feature = "azure_policy")]
903 if for_target {
904 // Resolve and validate target specifications across all modules
905 crate::interpreter::target::resolve::resolve_and_apply_target(
906 &mut self.interpreter,
907 )?;
908 // Infer resource types
909 crate::interpreter::target::infer::infer_resource_type(&mut self.interpreter)?;
910 }
911
912 if !for_target {
913 // Check if any module specifies a target and warn if so
914 #[cfg(feature = "azure_policy")]
915 self.warn_if_targets_present();
916 }
917
918 self.prepared = true;
919 }
920
921 Ok(())
922 }
923
924 #[doc(hidden)]
925 pub fn eval_rule_in_module(
926 &mut self,
927 module: &Ref<Module>,
928 rule: &Ref<Rule>,
929 enable_tracing: bool,
930 ) -> Result<Value> {
931 self.prepare_for_eval(enable_tracing, false)?;
932 self.interpreter.clean_internal_evaluation_state();
933
934 self.interpreter.eval_rule(module, rule)?;
935
936 Ok(self.interpreter.get_data_mut().clone())
937 }
938
939 #[doc(hidden)]
940 pub fn eval_modules(&mut self, enable_tracing: bool) -> Result<Value> {
941 self.prepare_for_eval(enable_tracing, false)?;
942 self.interpreter.clean_internal_evaluation_state();
943
944 // Ensure that empty modules are created.
945 for m in self.modules.iter().filter(|m| m.policy.is_empty()) {
946 let path = Parser::get_path_ref_components(&m.package.refr)?;
947 let path: Vec<&str> = path.iter().map(|s| s.text()).collect();
948 let vref =
949 Interpreter::make_or_get_value_mut(self.interpreter.get_data_mut(), &path[..])?;
950 if *vref == Value::Undefined {
951 *vref = Value::new_object();
952 }
953 }
954
955 self.interpreter.check_default_rules()?;
956 for module in self.modules.clone().iter() {
957 for rule in &module.policy {
958 self.interpreter.eval_rule(module, rule)?;
959 }
960 }
961 // Defer the evaluation of the default rules to here
962 for module in self.modules.clone().iter() {
963 let prev_module = self.interpreter.set_current_module(Some(module.clone()))?;
964 for rule in &module.policy {
965 self.interpreter.eval_default_rule(rule)?;
966 }
967 self.interpreter.set_current_module(prev_module)?;
968 }
969
970 // Ensure that all modules are created.
971 for m in self.modules.iter() {
972 let path = Parser::get_path_ref_components(&m.package.refr)?;
973 let path: Vec<&str> = path.iter().map(|s| s.text()).collect();
974 let vref =
975 Interpreter::make_or_get_value_mut(self.interpreter.get_data_mut(), &path[..])?;
976 if *vref == Value::Undefined {
977 *vref = Value::new_object();
978 }
979 }
980 self.interpreter.create_rule_prefixes()?;
981 Ok(self.interpreter.get_data_mut().clone())
982 }
983
984 /// Add a custom builtin (extension).
985 ///
986 /// * `path`: The fully qualified path of the builtin.
987 /// * `nargs`: The number of arguments the builtin takes.
988 /// * `extension`: The [`Extension`] instance.
989 ///
990 /// ```rust
991 /// # use regorus::*;
992 /// # use anyhow::{bail, Result};
993 /// # fn main() -> Result<()> {
994 /// let mut engine = Engine::new();
995 ///
996 /// // Policy uses `do_magic` custom builtin.
997 /// engine.add_policy(
998 /// "test.rego".to_string(),
999 /// r#"package test
1000 /// x = do_magic(1)
1001 /// "#.to_string(),
1002 /// )?;
1003 ///
1004 /// // Evaluating fails since `do_magic` is not defined.
1005 /// assert!(engine.eval_query("data.test.x".to_string(), false).is_err());
1006 ///
1007 /// // Add extension to implement `do_magic`. The extension can be stateful.
1008 /// let mut magic = 8;
1009 /// engine.add_extension("do_magic".to_string(), 1 , Box::new(move | mut params: Vec<Value> | {
1010 /// // params is mut and therefore individual values can be removed from it and modified.
1011 /// // The number of parameters (1) has already been validated.
1012 ///
1013 /// match ¶ms[0].as_i64() {
1014 /// Ok(i) => {
1015 /// // Compute value
1016 /// let v = *i + magic;
1017 /// // Update extension state.
1018 /// magic += 1;
1019 /// Ok(Value::from(v))
1020 /// }
1021 /// // Extensions can raise errors. Regorus will add location information to
1022 /// // the error.
1023 /// _ => bail!("do_magic expects i64 value")
1024 /// }
1025 /// }))?;
1026 ///
1027 /// // Evaluation will now succeed.
1028 /// let r = engine.eval_query("data.test.x".to_string(), false)?;
1029 /// assert_eq!(r.result[0].expressions[0].value.as_i64()?, 9);
1030 ///
1031 /// // Cloning the engine will also clone the extension.
1032 /// let mut engine1 = engine.clone();
1033 ///
1034 /// // Evaluating again will return a different value since the extension is stateful.
1035 /// let r = engine.eval_query("data.test.x".to_string(), false)?;
1036 /// assert_eq!(r.result[0].expressions[0].value.as_i64()?, 10);
1037 ///
1038 /// // The second engine has a clone of the extension.
1039 /// let r = engine1.eval_query("data.test.x".to_string(), false)?;
1040 /// assert_eq!(r.result[0].expressions[0].value.as_i64()?, 10);
1041 ///
1042 /// // Once added, the extension cannot be replaced or removed.
1043 /// assert!(engine.add_extension("do_magic".to_string(), 1, Box::new(|_:Vec<Value>| {
1044 /// Ok(Value::Undefined)
1045 /// })).is_err());
1046 ///
1047 /// // Extensions don't support out-parameter syntax.
1048 /// engine.add_policy(
1049 /// "policy.rego".to_string(),
1050 /// r#"package invalid
1051 /// x = y if {
1052 /// # y = do_magic(2)
1053 /// do_magic(2, y) # y is supplied as an out parameter.
1054 /// }
1055 /// "#.to_string()
1056 /// )?;
1057 ///
1058 /// // Evaluation fails since rule x calls an extension with out parameter.
1059 /// assert!(engine.eval_query("data.invalid.x".to_string(), false).is_err());
1060 /// # Ok(())
1061 /// # }
1062 /// ```
1063 pub fn add_extension(
1064 &mut self,
1065 path: String,
1066 nargs: u8,
1067 extension: Box<dyn Extension>,
1068 ) -> Result<()> {
1069 self.interpreter.add_extension(path, nargs, extension)
1070 }
1071
1072 #[cfg(feature = "coverage")]
1073 #[cfg_attr(docsrs, doc(cfg(feature = "coverage")))]
1074 /// Get the coverage report.
1075 ///
1076 /// ```rust
1077 /// # use regorus::*;
1078 /// # use anyhow::{bail, Result};
1079 /// # fn main() -> Result<()> {
1080 /// let mut engine = Engine::new();
1081 ///
1082 /// engine.add_policy(
1083 /// "policy.rego".to_string(),
1084 /// r#"
1085 /// package test # Line 2
1086 ///
1087 /// x = y if { # Line 4
1088 /// input.a > 2 # Line 5
1089 /// y = 5 # Line 6
1090 /// }
1091 /// "#.to_string()
1092 /// )?;
1093 ///
1094 /// // Enable coverage.
1095 /// engine.set_enable_coverage(true);
1096 ///
1097 /// engine.eval_query("data".to_string(), false)?;
1098 ///
1099 /// let report = engine.get_coverage_report()?;
1100 /// assert_eq!(report.files[0].path, "policy.rego");
1101 ///
1102 /// // Only line 5 is evaluated.
1103 /// assert_eq!(report.files[0].covered.iter().cloned().collect::<Vec<u32>>(), vec![5]);
1104 ///
1105 /// // Line 4 and 6 are not evaluated.
1106 /// assert_eq!(report.files[0].not_covered.iter().cloned().collect::<Vec<u32>>(), vec![4, 6]);
1107 /// # Ok(())
1108 /// # }
1109 /// ```
1110 ///
1111 /// See also [`crate::coverage::Report::to_colored_string`].
1112 pub fn get_coverage_report(&self) -> Result<crate::coverage::Report> {
1113 self.interpreter.get_coverage_report()
1114 }
1115
1116 #[cfg(feature = "coverage")]
1117 #[cfg_attr(docsrs, doc(cfg(feature = "coverage")))]
1118 /// Enable/disable policy coverage.
1119 ///
1120 /// If `enable` is different from the current value, then any existing coverage
1121 /// information will be cleared.
1122 pub fn set_enable_coverage(&mut self, enable: bool) {
1123 self.interpreter.set_enable_coverage(enable)
1124 }
1125
1126 #[cfg(feature = "coverage")]
1127 #[cfg_attr(docsrs, doc(cfg(feature = "coverage")))]
1128 /// Clear the gathered policy coverage data.
1129 pub fn clear_coverage_data(&mut self) {
1130 self.interpreter.clear_coverage_data()
1131 }
1132
1133 /// Gather output from print statements instead of emiting to stderr.
1134 ///
1135 /// See [`Engine::take_prints`].
1136 pub fn set_gather_prints(&mut self, b: bool) {
1137 self.interpreter.set_gather_prints(b);
1138 }
1139
1140 /// Take the gathered output of print statements.
1141 ///
1142 /// ```rust
1143 /// # use regorus::*;
1144 /// # use anyhow::{bail, Result};
1145 /// # fn main() -> Result<()> {
1146 /// let mut engine = Engine::new();
1147 ///
1148 /// // Print to stderr.
1149 /// engine.eval_query("print(\"Hello\")".to_string(), false)?;
1150 ///
1151 /// // Configure gathering print statements.
1152 /// engine.set_gather_prints(true);
1153 ///
1154 /// // Execute query.
1155 /// engine.eval_query("print(\"Hello\")".to_string(), false)?;
1156 ///
1157 /// // Take and clear prints.
1158 /// let prints = engine.take_prints()?;
1159 /// assert_eq!(prints.len(), 1);
1160 /// assert!(prints[0].contains("Hello"));
1161 ///
1162 /// for p in prints {
1163 /// println!("{p}");
1164 /// }
1165 /// # Ok(())
1166 /// # }
1167 /// ```
1168 pub fn take_prints(&mut self) -> Result<Vec<String>> {
1169 self.interpreter.take_prints()
1170 }
1171
1172 /// Get the policies and corresponding AST.
1173 ///
1174 ///
1175 /// ```rust
1176 /// # use regorus::*;
1177 /// # use anyhow::{bail, Result};
1178 /// # fn main() -> Result<()> {
1179 /// # let mut engine = Engine::new();
1180 /// engine.add_policy("test.rego".to_string(), "package test\n x := 1".to_string())?;
1181 ///
1182 /// let ast = engine.get_ast_as_json()?;
1183 /// let value = Value::from_json_str(&ast)?;
1184 ///
1185 /// assert_eq!(value[0]["ast"]["package"]["refr"]["Var"][1].as_string()?.as_ref(), "test");
1186 /// # Ok(())
1187 /// # }
1188 /// ```
1189 #[cfg(feature = "ast")]
1190 #[cfg_attr(docsrs, doc(cfg(feature = "ast")))]
1191 pub fn get_ast_as_json(&self) -> Result<String> {
1192 #[derive(Serialize)]
1193 struct Policy<'a> {
1194 source: &'a Source,
1195 version: u32,
1196 ast: &'a Module,
1197 }
1198 let mut ast = vec![];
1199 for m in self.modules.iter() {
1200 ast.push(Policy {
1201 source: &m.package.span.source,
1202 version: 1,
1203 ast: m,
1204 });
1205 }
1206
1207 serde_json::to_string_pretty(&ast).map_err(anyhow::Error::msg)
1208 }
1209
1210 /// Get the package names of each policy added to the engine.
1211 ///
1212 ///
1213 /// ```rust
1214 /// # use regorus::*;
1215 /// # use anyhow::{bail, Result};
1216 /// # fn main() -> Result<()> {
1217 /// # let mut engine = Engine::new();
1218 /// engine.add_policy("test.rego".to_string(), "package test\n x := 1".to_string())?;
1219 /// engine.add_policy("test2.rego".to_string(), "package test.multi.segment\n x := 1".to_string())?;
1220 ///
1221 /// let package_names = engine.get_policy_package_names()?;
1222 ///
1223 /// assert_eq!("test", package_names[0].package_name);
1224 /// assert_eq!("test.multi.segment", package_names[1].package_name);
1225 /// # Ok(())
1226 /// # }
1227 /// ```
1228 #[cfg(feature = "azure_policy")]
1229 #[cfg_attr(docsrs, doc(cfg(feature = "azure_policy")))]
1230 pub fn get_policy_package_names(&self) -> Result<Vec<PolicyPackageNameDefinition>> {
1231 let mut package_names = vec![];
1232 for m in self.modules.iter() {
1233 let package_name = Interpreter::get_path_string(&m.package.refr, None)?;
1234 package_names.push(PolicyPackageNameDefinition {
1235 source_file: m.package.span.source.file().to_string(),
1236 package_name,
1237 });
1238 }
1239
1240 Ok(package_names)
1241 }
1242
1243 /// Get the parameters defined in each policy.
1244 ///
1245 ///
1246 /// ```rust
1247 /// # use regorus::*;
1248 /// # use anyhow::{bail, Result};
1249 /// # fn main() -> Result<()> {
1250 /// # let mut engine = Engine::new();
1251 /// engine.add_policy("test.rego".to_string(), "package test default parameters.a = 5 parameters.b = 10\n x := 1".to_string())?;
1252 ///
1253 /// let parameters = engine.get_policy_parameters()?;
1254 ///
1255 /// assert_eq!("a", parameters[0].parameters[0].name);
1256 /// assert_eq!("b", parameters[0].modifiers[0].name);
1257 ///
1258 /// # Ok(())
1259 /// # }
1260 /// ```
1261 #[cfg(feature = "azure_policy")]
1262 #[cfg_attr(docsrs, doc(cfg(feature = "azure_policy")))]
1263 pub fn get_policy_parameters(&self) -> Result<Vec<PolicyParameters>> {
1264 let mut policy_parameter_definitions = vec![];
1265 for m in self.modules.iter() {
1266 let mut parameters = vec![];
1267 let mut modifiers = vec![];
1268
1269 for rule in &m.policy {
1270 // Extract parameter definitions from the policy rule
1271 // e.g. default parameters.a = 5
1272 if let Rule::Default { refr, .. } = rule.as_ref() {
1273 let path = Parser::get_path_ref_components(refr)?;
1274 let paths: Vec<&str> = path.iter().map(|s| s.text()).collect();
1275
1276 if paths.len() == 2 && paths[0] == "parameters" {
1277 // Todo: Fetch fields other than name from rego metadoc for the parameter
1278 parameters.push(PolicyParameter {
1279 name: paths[1].to_string(),
1280 modifiable: false,
1281 required: false,
1282 })
1283 }
1284 }
1285
1286 // Extract modifiers to the parameters from the policy rule
1287 // e.g. parameters.a = 5
1288 if let Rule::Spec { head, .. } = rule.as_ref() {
1289 match head {
1290 RuleHead::Compr { refr, .. } => {
1291 let path = Parser::get_path_ref_components(refr)?;
1292 let paths: Vec<&str> = path.iter().map(|s| s.text()).collect();
1293
1294 if paths.len() == 2 && paths[0] == "parameters" {
1295 // Todo: Fetch fields other than name from rego metadoc for the parameter
1296 modifiers.push(PolicyModifier {
1297 name: paths[1].to_string(),
1298 })
1299 }
1300 }
1301 RuleHead::Func { .. } => {}
1302 RuleHead::Set { .. } => {}
1303 }
1304 }
1305 }
1306
1307 policy_parameter_definitions.push(PolicyParameters {
1308 source_file: m.package.span.source.file().to_string(),
1309 parameters,
1310 modifiers,
1311 });
1312 }
1313
1314 Ok(policy_parameter_definitions)
1315 }
1316
1317 /// Emit a warning if any modules contain target specifications but we're not using target-aware compilation.
1318 #[cfg(feature = "azure_policy")]
1319 fn warn_if_targets_present(&self) {
1320 let mut has_target = false;
1321 let mut target_files = Vec::new();
1322
1323 for module in self.modules.iter() {
1324 if module.target.is_some() {
1325 has_target = true;
1326 target_files.push(module.package.span.source.get_path());
1327 }
1328 }
1329
1330 if has_target {
1331 std::eprintln!("Warning: Target specifications found in policy modules but not using target-aware compilation.");
1332 std::eprintln!(" The following files contain __target__ declarations:");
1333 for file in target_files {
1334 std::eprintln!(" - {}", file);
1335 }
1336 std::eprintln!(" Consider using compile_for_target() instead of compile_with_entrypoint() for target-aware evaluation.");
1337 }
1338 }
1339
1340 fn make_parser<'a>(&self, source: &'a Source) -> Result<Parser<'a>> {
1341 let mut parser = Parser::new(source)?;
1342 if self.rego_v1 {
1343 parser.enable_rego_v1()?;
1344 }
1345 Ok(parser)
1346 }
1347
1348 /// Create a new Engine from a compiled policy.
1349 #[doc(hidden)]
1350 pub(crate) fn new_from_compiled_policy(
1351 compiled_policy: Rc<crate::compiled_policy::CompiledPolicyData>,
1352 ) -> Self {
1353 let modules = compiled_policy.modules.clone();
1354 Self {
1355 modules,
1356 interpreter: Interpreter::new_from_compiled_policy(compiled_policy),
1357 rego_v1: true, // Value doesn't matter since this is used only for policy parsing
1358 prepared: true,
1359 }
1360 }
1361}