Skip to content

Commit 4ba94b8

Browse files
anakrishCopilot
andcommitted
refactor(languages,schema): migrate to Object/Set API
Migrates azure_policy (aliases + compiler + denormalizer + normalizer), azure_rbac (builtins/lists), Rego (compiler/collection literals), and schema/validate to the new Object/Set API. Notable cleanups: - azure_policy/aliases/obj_map.rs: switch through Object::as_ref / Object::as_mut instead of escape hatches. - azure_policy/compiler/effects.rs: wrap an already-built Object directly via Rc::new(template) instead of draining + rebuilding via Object::from_iter(template). - rego/compiler/expressions/collection_literals.rs: hoist constant collections as Object/Set literals. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3979082 commit 4ba94b8

11 files changed

Lines changed: 56 additions & 74 deletions

File tree

src/languages/azure_policy/aliases/denormalizer/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,10 @@ pub fn denormalize_with_aliases(
213213
// Phase 4: Attach properties to result.
214214
if !properties.is_empty() {
215215
if let Some(Value::Object(existing_rc)) = result.get_mut("properties") {
216-
// Merge directly into the BTreeMap, avoiding full ObjMap round-trip.
216+
// Merge directly into the Object, avoiding full ObjMap round-trip.
217217
let existing = Rc::make_mut(existing_rc);
218218
for (k, v) in properties {
219-
existing.entry(Value::String(k)).or_insert(v);
219+
existing.get_or_insert_with(Value::String(k), || v);
220220
}
221221
} else {
222222
obj_insert(&mut result, "properties", make_value(properties));

src/languages/azure_policy/aliases/denormalizer/sub_resource.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use alloc::collections::{BTreeMap, BTreeSet};
77
use alloc::string::String;
88
use alloc::vec::Vec;
99

10+
use crate::collections::Object;
1011
use crate::Value;
1112

1213
use super::super::obj_map::{make_value, new_map, obj_insert, val_str, ObjMap};
@@ -141,7 +142,7 @@ fn rewrap_nested_array(
141142
/// BTreeMap-native recursion for nested sub-resource array re-wrapping,
142143
/// avoiding ObjMap round-trips on each array element.
143144
fn rewrap_nested_array_in_btree(
144-
btree: &mut alloc::collections::BTreeMap<Value, Value>,
145+
btree: &mut Object,
145146
parent_parts: &[&str],
146147
array_name: &str,
147148
envelope_fields: &BTreeSet<String>,
@@ -187,10 +188,7 @@ fn rewrap_nested_array_in_btree(
187188
}
188189

189190
/// Find a key in a BTreeMap using case-insensitive comparison.
190-
fn find_key_ci_btree(
191-
btree: &alloc::collections::BTreeMap<Value, Value>,
192-
key: &str,
193-
) -> Option<Value> {
191+
fn find_key_ci_btree(btree: &Object, key: &str) -> Option<Value> {
194192
btree
195193
.keys()
196194
.find(|k| val_str(k).is_some_and(|s| s.eq_ignore_ascii_case(key)))

src/languages/azure_policy/aliases/normalizer/element_remap.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use alloc::string::String;
77
use alloc::vec::Vec;
88

9+
use crate::collections::Object;
910
use crate::Value;
1011

1112
use super::super::obj_map::{
@@ -118,7 +119,7 @@ fn apply_remap_at_depth(
118119
/// BTreeMap-native recursion for element-level remap, avoiding ObjMap
119120
/// round-trips on each array element.
120121
fn remap_at_depth_in_btree(
121-
btree: &mut alloc::collections::BTreeMap<Value, Value>,
122+
btree: &mut Object,
122123
array_chain: &[Vec<String>],
123124
depth: usize,
124125
source_field: &str,
@@ -177,12 +178,7 @@ fn remap_at_depth_in_btree(
177178
}
178179

179180
/// Remap a value between dotted paths directly in a BTreeMap.
180-
fn remap_deep_field_in_btree(
181-
btree: &mut alloc::collections::BTreeMap<Value, Value>,
182-
source: &str,
183-
target: &str,
184-
lowercase: bool,
185-
) {
181+
fn remap_deep_field_in_btree(btree: &mut Object, source: &str, target: &str, lowercase: bool) {
186182
let val = match read_dotted_path_btree(btree, source) {
187183
Some(v) => v,
188184
None => return,
@@ -202,10 +198,7 @@ fn remap_deep_field_in_btree(
202198
}
203199

204200
/// Read a value at a dotted path from a BTreeMap.
205-
fn read_dotted_path_btree(
206-
btree: &alloc::collections::BTreeMap<Value, Value>,
207-
path: &str,
208-
) -> Option<Value> {
201+
fn read_dotted_path_btree(btree: &Object, path: &str) -> Option<Value> {
209202
let segments: Vec<&str> = path.split('.').collect();
210203
let first = segments.first()?;
211204
let mut cur: &Value = btree.get(&Value::from(*first))?;

src/languages/azure_policy/aliases/normalizer/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod flatten;
1313
// Re-export items used by the denormalizer.
1414
pub(crate) use element_remap::{apply_element_remap, ElementRemap};
1515

16+
use crate::collections::Object;
1617
use crate::Value;
1718

1819
use super::obj_map::{
@@ -109,7 +110,7 @@ pub fn normalize_with_aliases(
109110
/// Merge `properties` fields into the result map, skipping keys that already
110111
/// exist.
111112
fn merge_properties(
112-
obj: &alloc::collections::BTreeMap<Value, Value>,
113+
obj: &Object,
113114
result: &mut ObjMap,
114115
sub_arrays: Option<&alloc::collections::BTreeSet<alloc::string::String>>,
115116
) {

src/languages/azure_policy/aliases/obj_map.rs

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
//! Lightweight string-keyed map used during normalization/denormalization.
55
//!
66
//! Internally uses `hashbrown::HashMap<Rc<str>, Value>` for O(1) lookups,
7-
//! then converts to `Value::Object` (a `BTreeMap<Value, Value>`) only at
7+
//! then converts to `Value::Object` (an `Object`) only at
88
//! the output boundary via [`make_value`].
99
1010
use alloc::string::String;
1111
use alloc::vec::Vec;
1212

1313
use hashbrown::HashMap;
1414

15+
use crate::collections::Object;
1516
use crate::Rc;
1617
use crate::Value;
1718

@@ -81,14 +82,13 @@ pub fn obj_remove(map: &mut ObjMap, key: &str) -> Option<Value> {
8182
/// Convert an [`ObjMap`] into a [`Value::Object`].
8283
///
8384
/// Keys are converted from `Rc<str>` to `Value::String` and inserted into
84-
/// a `BTreeMap` to match the `Value::Object` representation.
85+
/// an `Object` to match the `Value::Object` representation.
8586
pub fn make_value(map: ObjMap) -> Value {
86-
use alloc::collections::BTreeMap;
87-
let mut btree = BTreeMap::new();
88-
for (k, v) in map {
89-
btree.insert(Value::String(k), v);
90-
}
91-
Value::Object(Rc::new(btree))
87+
let obj: Object = map
88+
.into_iter()
89+
.map(|(k, v)| (Value::String(k), v))
90+
.collect();
91+
Value::Object(Rc::new(obj))
9292
}
9393

9494
/// Convert a `Vec<Value>` into a `Value::Array`.
@@ -115,14 +115,14 @@ pub fn extract_type_field(resource: &Value) -> Option<&str> {
115115
})
116116
}
117117

118-
/// Convert a `Value::Object` (BTreeMap<Value, Value>) into an [`ObjMap`].
118+
/// Convert a `Value::Object` (Object) into an [`ObjMap`].
119119
///
120120
/// Non-string keys are silently skipped.
121121
#[allow(dead_code)]
122122
pub fn value_to_obj_map(value: &Value) -> Option<ObjMap> {
123-
let btree = value.as_object().ok()?;
124-
let mut map = ObjMap::with_capacity(btree.len());
125-
for (k, v) in btree.iter() {
123+
let obj = value.as_object().ok()?;
124+
let mut map = ObjMap::with_capacity(obj.len());
125+
for (k, v) in obj.iter() {
126126
if let Value::String(s) = k {
127127
map.insert(Rc::clone(s), v.clone());
128128
}
@@ -203,17 +203,12 @@ fn set_nested_inner(obj: &mut ObjMap, segments: &[&str], value: Value, lowercase
203203
}
204204
}
205205

206-
/// Set a value at a path directly in a `BTreeMap<Value, Value>`, creating
206+
/// Set a value at a path directly in an `Object`, creating
207207
/// intermediate `Value::Object` nodes as needed.
208208
///
209209
/// This avoids the `btree_to_obj_map` / `obj_map_to_btree` round-trip that
210210
/// would clone every sibling entry at each nesting level.
211-
pub fn set_nested_in_btree(
212-
btree: &mut alloc::collections::BTreeMap<Value, Value>,
213-
segments: &[&str],
214-
value: Value,
215-
lowercase: bool,
216-
) {
211+
pub fn set_nested_in_btree(obj: &mut Object, segments: &[&str], value: Value, lowercase: bool) {
217212
let Some(&first) = segments.first() else {
218213
return;
219214
};
@@ -226,16 +221,16 @@ pub fn set_nested_in_btree(
226221
let key_val = Value::String(Rc::clone(&key_rc));
227222

228223
if segments.len() == 1 {
229-
btree.insert(key_val, value);
224+
obj.insert(key_val, value);
230225
return;
231226
}
232227

233228
// Ensure an intermediate object exists.
234-
if !btree.contains_key(&key_val) {
235-
btree.insert(key_val.clone(), make_value(new_map()));
229+
if !obj.contains_key(&key_val) {
230+
obj.insert(key_val.clone(), make_value(new_map()));
236231
}
237232

238-
if let Some(Value::Object(inner_rc)) = btree.get_mut(&key_val) {
233+
if let Some(Value::Object(inner_rc)) = obj.get_mut(&key_val) {
239234
let inner = Rc::make_mut(inner_rc);
240235
set_nested_in_btree(
241236
inner,
@@ -363,9 +358,9 @@ fn remove_field_at_depth(obj: &mut ObjMap, array_chain: &[Vec<String>], depth: u
363358
}
364359
}
365360

366-
/// BTreeMap-native recursion for element-level field removal.
361+
/// Object-native recursion for element-level field removal.
367362
fn remove_field_at_depth_in_btree(
368-
btree: &mut alloc::collections::BTreeMap<Value, Value>,
363+
obj: &mut Object,
369364
array_chain: &[Vec<String>],
370365
depth: usize,
371366
field: &str,
@@ -374,10 +369,10 @@ fn remove_field_at_depth_in_btree(
374369
let segments: Vec<&str> = field.split('.').collect();
375370
if segments.len() == 1 {
376371
if let Some(&seg) = segments.first() {
377-
btree.remove(&Value::from(seg));
372+
obj.remove(&Value::from(seg));
378373
}
379374
} else if segments.len() > 1 {
380-
remove_at_dotted_path_in_btree(btree, &segments);
375+
remove_at_dotted_path_in_btree(obj, &segments);
381376
}
382377
return;
383378
};
@@ -389,12 +384,12 @@ fn remove_field_at_depth_in_btree(
389384

390385
let key_val = Value::from(first);
391386
let arr_val = if nav.len() == 1 {
392-
match btree.get_mut(&key_val) {
387+
match obj.get_mut(&key_val) {
393388
Some(v) => v,
394389
None => return,
395390
}
396391
} else {
397-
let mut cur: &mut Value = match btree.get_mut(&key_val) {
392+
let mut cur: &mut Value = match obj.get_mut(&key_val) {
398393
Some(v) => v,
399394
None => return,
400395
};
@@ -426,24 +421,21 @@ fn remove_field_at_depth_in_btree(
426421
}
427422
}
428423

429-
/// Remove the leaf segment at a dotted path directly in a BTreeMap.
430-
fn remove_at_dotted_path_in_btree(
431-
btree: &mut alloc::collections::BTreeMap<Value, Value>,
432-
segments: &[&str],
433-
) {
424+
/// Remove the leaf segment at a dotted path directly in an Object.
425+
fn remove_at_dotted_path_in_btree(obj: &mut Object, segments: &[&str]) {
434426
let Some((&leaf, parent_segs)) = segments.split_last() else {
435427
return;
436428
};
437429
if parent_segs.is_empty() {
438-
btree.remove(&Value::from(leaf));
430+
obj.remove(&Value::from(leaf));
439431
return;
440432
}
441433

442434
let Some(&first) = parent_segs.first() else {
443435
return;
444436
};
445437
let first_key = Value::from(first);
446-
let parent_val = match btree.get_mut(&first_key) {
438+
let parent_val = match obj.get_mut(&first_key) {
447439
Some(v) => v,
448440
None => return,
449441
};

src/languages/azure_policy/compiler/effects.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//! to fetch a related resource and an optional `existenceCondition` evaluated
1212
//! inline.
1313
14-
use alloc::collections::BTreeMap;
14+
use crate::collections::Object;
1515
use alloc::format;
1616
use alloc::string::ToString as _;
1717
use alloc::vec::Vec;
@@ -814,7 +814,7 @@ pub(super) fn build_object_from_keys(
814814
span: &crate::lexer::Span,
815815
) -> Result<u8> {
816816
// Build template: object with all keys set to Undefined.
817-
let mut template = BTreeMap::new();
817+
let mut template = Object::new();
818818
for &(key_idx, _) in &keys {
819819
// key_idx was returned by `add_literal_u16` in the calling code,
820820
// so it is always in bounds. We use `.get()` + `?` instead of

src/languages/azure_policy/compiler/metadata.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
//! observations into the program's metadata so the runtime can inspect
1111
//! them without re-analysing the AST.
1212
13-
use alloc::collections::BTreeSet;
13+
use crate::collections::Set;
1414
use alloc::string::{String, ToString as _};
1515

1616
use crate::languages::azure_policy::ast::{
@@ -232,7 +232,7 @@ impl Compiler {
232232

233233
// Parameter names.
234234
if !defn.parameters.is_empty() {
235-
let set: BTreeSet<Value> = defn
235+
let set: Set = defn
236236
.parameters
237237
.iter()
238238
.map(|p| Value::String(p.name.as_str().into()))
@@ -272,10 +272,10 @@ impl Compiler {
272272
fn insert_string_set_annotation(
273273
annot: &mut alloc::collections::BTreeMap<String, Value>,
274274
key: &str,
275-
observed: &BTreeSet<String>,
275+
observed: &alloc::collections::BTreeSet<String>,
276276
) {
277277
if !observed.is_empty() {
278-
let set: BTreeSet<Value> = observed
278+
let set: Set = observed
279279
.iter()
280280
.map(|s| Value::String(s.as_str().into()))
281281
.collect();

src/languages/azure_rbac/builtins/lists.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
use alloc::collections::BTreeSet;
5-
4+
use crate::collections::Set;
65
use crate::value::Value;
76

87
use super::evaluator::RbacBuiltinError;
@@ -28,7 +27,7 @@ fn list_contains_values(list: &[Value], needle: &Value) -> bool {
2827
}
2928

3029
// For sets, treat a list/set needle as "all elements are contained".
31-
fn set_contains_values(set: &BTreeSet<Value>, needle: &Value) -> bool {
30+
fn set_contains_values(set: &Set, needle: &Value) -> bool {
3231
match *needle {
3332
// For collection needles, require all elements to be present.
3433
Value::Array(ref right_list) => right_list.iter().all(|item| set.contains(item)),

src/languages/rego/compiler/expressions/collection_literals.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88

99
use super::{Compiler, Register, Result};
1010
use crate::ast::{Expr, ExprRef};
11+
use crate::collections::{Object, Set};
1112
use crate::lexer::Span;
1213
use crate::rvm::instructions::{ArrayCreateParams, ObjectCreateParams, SetCreateParams};
1314
use crate::rvm::Instruction;
1415
use crate::{Rc, Value};
15-
use alloc::collections::{BTreeMap, BTreeSet};
1616
use alloc::vec::Vec;
1717

1818
/// Try to evaluate an expression as a compile-time constant.
@@ -38,12 +38,12 @@ pub(in crate::languages::rego::compiler) fn try_eval_const(expr: &Expr) -> Optio
3838
Expr::Set { items, .. } => items
3939
.iter()
4040
.map(|i| try_eval_const(i.as_ref()))
41-
.collect::<Option<BTreeSet<_>>>()
41+
.collect::<Option<Set>>()
4242
.map(|s| Value::Set(Rc::new(s))),
4343
Expr::Object { fields, .. } => fields
4444
.iter()
4545
.map(|(_, k, v)| Some((try_eval_const(k.as_ref())?, try_eval_const(v.as_ref())?)))
46-
.collect::<Option<BTreeMap<_, _>>>()
46+
.collect::<Option<Object>>()
4747
.map(|m| Value::Object(Rc::new(m))),
4848
_ => None,
4949
}
@@ -87,8 +87,7 @@ impl<'a> Compiler<'a> {
8787
items: &[ExprRef],
8888
span: &Span,
8989
) -> Result<Register> {
90-
let all_const: Option<BTreeSet<_>> =
91-
items.iter().map(|i| try_eval_const(i.as_ref())).collect();
90+
let all_const: Option<Set> = items.iter().map(|i| try_eval_const(i.as_ref())).collect();
9291
if let Some(values) = all_const {
9392
let dest = self.alloc_register();
9493
let literal_idx = self.add_literal(Value::Set(Rc::new(values)));
@@ -117,7 +116,7 @@ impl<'a> Compiler<'a> {
117116
fields: &[(crate::lexer::Span, ExprRef, ExprRef)],
118117
span: &Span,
119118
) -> Result<Register> {
120-
let all_const: Option<BTreeMap<_, _>> = fields
119+
let all_const: Option<Object> = fields
121120
.iter()
122121
.map(|(_, k, v)| Some((try_eval_const(k.as_ref())?, try_eval_const(v.as_ref())?)))
123122
.collect();
@@ -166,7 +165,7 @@ impl<'a> Compiler<'a> {
166165
let mut template_keys = literal_keys.clone();
167166
template_keys.sort();
168167

169-
let mut template_obj = BTreeMap::new();
168+
let mut template_obj = Object::new();
170169
for key in &template_keys {
171170
template_obj.insert(key.clone(), Value::Undefined);
172171
}

0 commit comments

Comments
 (0)