11// Copyright (c) Microsoft Corporation.
22// Licensed under the MIT License.
33
4- //! Core recursive -descent JSON parser for Azure Policy.
4+ //! Recursive -descent JSON parser for Azure Policy rule constraints .
55//!
6- //! Provides the low-level token-driven parser (`core::Parser`) that reads JSON from
7- //! [`Lexer`] tokens, building span-annotated AST values in a single pass.
8- //! No intermediate `serde_json::Value` is created.
6+ //! Parses Azure Policy JSON directly from [`Lexer`] tokens, building span-annotated
7+ //! AST nodes in a single pass. No intermediate `serde_json::Value` is created.
98//!
10- //! Higher-level policy-aware parsing (constraints, policy rules, policy
11- //! definitions) is layered on top by sibling modules.
9+ //! The parser is policy-aware: when parsing JSON objects, it dispatches on key names
10+ //! (`allOf`, `anyOf`, `not`, `field`, `value`, `count`, operator names) to build
11+ //! the appropriate AST nodes.
1212
13- // Parser internals are consumed by constraint/policy_rule/policy_definition
14- // modules added in a subsequent PR.
15- #[ allow( dead_code) ]
16- pub ( crate ) mod core;
13+ mod constraint;
14+ mod core;
1715mod error;
1816
1917pub ( super ) use self :: core:: json_unescape;
2018pub use error:: ParseError ;
2119
2220use alloc:: string:: ToString as _;
2321
24- use super :: ast:: { FieldKind , OperatorKind } ;
22+ use crate :: lexer:: { Source , TokenKind } ;
23+
24+ use super :: ast:: { Constraint , FieldKind , OperatorKind } ;
2525use super :: expr:: ExprParser ;
2626
27+ use self :: core:: Parser ;
28+
29+ // ============================================================================
30+ // Public API
31+ // ============================================================================
32+
33+ /// Parse a standalone constraint from a JSON source.
34+ ///
35+ /// A constraint is one of:
36+ /// - Logical combinator: `{ "allOf": [...] }`, `{ "anyOf": [...] }`, `{ "not": {...} }`
37+ /// - Leaf condition: `{ "field": "...", "equals": "..." }`
38+ /// - Count condition: `{ "count": { "field": "..." }, "greater": 0 }`
39+ pub fn parse_constraint ( source : & Source ) -> Result < Constraint , ParseError > {
40+ let mut parser = Parser :: new ( source) ?;
41+ let constraint = parser. parse_constraint ( ) ?;
42+
43+ if parser. tok . 0 != TokenKind :: Eof {
44+ return Err ( ParseError :: UnexpectedToken {
45+ span : parser. tok . 1 . clone ( ) ,
46+ expected : "end of input" ,
47+ } ) ;
48+ }
49+
50+ Ok ( constraint)
51+ }
52+
2753// ============================================================================
28- // Helper functions (used by constraint/policy_rule/policy_definition modules )
54+ // Helper functions (used across submodules )
2955// ============================================================================
3056
3157/// Check if a string is an ARM template expression (`[...]` but not `[[...`).
32- #[ allow( dead_code) ]
3358pub ( super ) fn is_template_expr ( s : & str ) -> bool {
3459 s. starts_with ( '[' ) && s. ends_with ( ']' ) && !s. starts_with ( "[[" )
3560}
@@ -51,7 +76,6 @@ fn unwrap(s: &str, prefix_len: usize, suffix_len: usize) -> &str {
5176}
5277
5378/// Classify a field string into a [`FieldKind`].
54- #[ allow( dead_code) ]
5579pub ( super ) fn classify_field (
5680 text : & str ,
5781 span : & crate :: lexer:: Span ,
@@ -96,7 +120,6 @@ pub(super) fn classify_field(
96120}
97121
98122/// Try to parse a lowercase key as an operator kind.
99- #[ allow( dead_code) ]
100123pub ( super ) fn parse_operator_kind ( key : & str ) -> Option < OperatorKind > {
101124 match key {
102125 "contains" => Some ( OperatorKind :: Contains ) ,
0 commit comments