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:
Anand Krishnamoorthi
2026-04-23 11:53:43 -05:00
committed by GitHub
parent f50a9744ff
commit ad82227ddb
7 changed files with 1951 additions and 78 deletions
+4 -5
View File
@@ -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"));
}
// -----------------------------------------------------------------------