mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat(azure-policy): implement count/count.where compilation (#688)
Implement the full count loop compiler, replacing the stubs in count.rs, count_any.rs, and count_bindings.rs with a single consolidated module. Handles both field-based and value-based count nodes. Field counts walk the resource via resolve_alias_path then iterate the wildcard array; value counts operate on an arbitrary collection expression. For nested wildcard paths like A[*].B[*].C, the compiler emits recursive ForEach loops, drilling one wildcard level at a time. When an outer count binding already covers a prefix, the inner loop starts from the bound element register instead of re-walking from the resource root. Existence patterns (count > 0, count == 0) are recognized and lowered to LoopMode::Any, which exits on the first match rather than counting every element. Count-binding resolution threads the current-element register through inner field references and current() calls so that nested conditions can address fields relative to the loop variable. Also fixes the bound_len arithmetic in conditions_wildcard.rs with a cleaner strip_prefix call, and removes the nested-wildcard bail in split_count_wildcard_path since the compiler now handles them.
This commit is contained in:
committed by
GitHub
parent
f50a9744ff
commit
ad82227ddb
@@ -4,6 +4,7 @@
|
||||
|
||||
//! Implicit allOf for unbound `[*]` wildcard fields.
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::{String, ToString as _};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
@@ -67,12 +68,12 @@ impl Compiler {
|
||||
};
|
||||
|
||||
if let Some(prefix) = &binding.field_wildcard_prefix {
|
||||
let bound_len = prefix.len().saturating_add(4); // len("prefix") + len("[*].")
|
||||
if path.len() > bound_len {
|
||||
if let Some(remainder) = path.get(bound_len..) {
|
||||
if remainder.contains("[*]") {
|
||||
return Ok(Some((binding, remainder.to_string())));
|
||||
}
|
||||
let lc_prefix = prefix.to_ascii_lowercase();
|
||||
let bound_prefix = format!("{}[*].", lc_prefix);
|
||||
if let Some(remainder) = path.to_ascii_lowercase().strip_prefix(&bound_prefix) {
|
||||
let remainder = remainder.to_string();
|
||||
if remainder.contains("[*]") {
|
||||
return Ok(Some((binding, remainder)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
//! Existence-pattern optimization (count → Any loop).
|
||||
//!
|
||||
//! Stub — real implementation added in a later commit.
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
#![allow(dead_code)]
|
||||
|
||||
//! Count-binding resolution and `current()` references.
|
||||
//!
|
||||
//! Stub — real implementation added in a later commit.
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use super::core::{Compiler, CountBinding};
|
||||
|
||||
impl Compiler {
|
||||
pub(super) const fn resolve_count_binding(
|
||||
&self,
|
||||
_field_path: &str,
|
||||
) -> Result<Option<CountBinding>> {
|
||||
_ = self.register_counter;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub(super) fn compile_from_binding(
|
||||
&mut self,
|
||||
_binding: &CountBinding,
|
||||
_field_path: &str,
|
||||
span: &crate::lexer::Span,
|
||||
) -> Result<u8> {
|
||||
let _ = self;
|
||||
bail!(span.error("count binding compilation not yet implemented"))
|
||||
}
|
||||
|
||||
pub(super) fn compile_current_reference(
|
||||
&mut self,
|
||||
_key: &str,
|
||||
span: &crate::lexer::Span,
|
||||
) -> Result<u8> {
|
||||
let _ = self;
|
||||
bail!(span.error("current() reference not yet implemented"))
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,8 @@
|
||||
//! - [`core`]: `Compiler` struct, main pipeline, register/emit helpers
|
||||
//! - [`conditions`]: constraint / condition / LHS compilation
|
||||
//! - [`conditions_wildcard`]: implicit allOf for unbound `[*]` fields
|
||||
//! - [`count`]: `count` / `count.where` loops
|
||||
//! - [`count_any`]: existence-pattern optimization (count → Any loop)
|
||||
//! - [`count_bindings`]: count-binding resolution and `current()` references
|
||||
//! - [`count`]: `count` / `count.where` loops, existence-pattern optimization,
|
||||
//! count-binding resolution and `current()` references
|
||||
//! - [`expressions`]: template-expression and call-expression compilation
|
||||
//! - [`fields`]: field-kind and resource-path compilation
|
||||
//! - [`template_dispatch`]: ARM template function dispatch
|
||||
@@ -23,8 +22,6 @@ mod conditions;
|
||||
mod conditions_wildcard;
|
||||
mod core;
|
||||
mod count;
|
||||
mod count_any;
|
||||
mod count_bindings;
|
||||
mod effects;
|
||||
mod effects_modify_append;
|
||||
mod expressions;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
#![allow(dead_code, clippy::pattern_type_mismatch)]
|
||||
#![allow(clippy::pattern_type_mismatch)]
|
||||
|
||||
//! Free helper functions used by the Azure Policy compiler.
|
||||
|
||||
@@ -43,9 +43,6 @@ pub(super) fn split_count_wildcard_path(path: &str) -> Result<(String, Option<St
|
||||
path
|
||||
)
|
||||
})?;
|
||||
if after_wildcard.contains("[*]") {
|
||||
bail!("nested [*] wildcards are not supported: {}", path);
|
||||
}
|
||||
let suffix_str = after_wildcard.trim_start_matches('.');
|
||||
let suffix = if suffix_str.is_empty() {
|
||||
None
|
||||
@@ -351,7 +348,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn wildcard_nested() {
|
||||
split_count_wildcard_path("a[*].b[*].c").unwrap_err();
|
||||
let (prefix, suffix) = split_count_wildcard_path("a[*].b[*].c").unwrap();
|
||||
assert_eq!(prefix, "a");
|
||||
assert_eq!(suffix.as_deref(), Some("b[*].c"));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user