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 &params[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}