Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions bindings/python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,16 +163,16 @@ fn to(mut v: Value, py: Python<'_>) -> Result<Py<PyAny>> {

Value::Set(_) => {
let set = PySet::empty(py)?;
for v in std::mem::take(v.as_set_mut()?) {
set.add(to(v, py)?)?;
for item in v.set_ref()?.iter() {
set.add(to(item.clone(), py)?)?;
}
set.into_bound_py_any(py)
}

Value::Object(_) => {
let dict = PyDict::new(py);
for (k, v) in std::mem::take(v.as_object_mut()?) {
dict.set_item(to(k, py)?, to(v, py)?)?;
for (k, val) in v.object_ref()?.iter() {
dict.set_item(to(k.clone(), py)?, to(val.clone(), py)?)?;
}
dict.into_bound_py_any(py)
}
Expand Down
20 changes: 10 additions & 10 deletions examples/regorus/azure_policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,16 @@ pub fn azure_policy_eval(
let raw_resource = Value::from_json_str(&resource_json)?;
let normalized = normalizer::normalize(&raw_resource, Some(&registry), api_version.as_deref());
println!("Normalized resource ({} top-level fields)", {
normalized.as_object().map(|m| m.len()).unwrap_or(0)
normalized.object_ref().map(|m| m.len()).unwrap_or(0)
});

// Inject api_version into the normalized resource (lowercased key to match
// the host contract — policies reference `field('apiVersion')` which the
// compiler lowercases to `apiversion`).
let mut resource = normalized;
if let Some(ref api_ver) = api_version {
let map = resource.as_object_mut()?;
map.insert(Value::from("apiversion"), Value::from(api_ver.clone()));
let mut map = resource.object_ref_mut()?;
map.insert(Value::from("apiversion"), Value::from(api_ver.clone()))?;
}

// Build the input envelope: { resource, parameters }
Expand All @@ -89,9 +89,9 @@ pub fn azure_policy_eval(
};
let mut input = Value::new_object();
{
let map = input.as_object_mut()?;
map.insert(Value::from("resource"), resource);
map.insert(Value::from("parameters"), parameters);
let mut map = input.object_ref_mut()?;
map.insert(Value::from("resource"), resource)?;
map.insert(Value::from("parameters"), parameters)?;
}

// Build a default context with requestContext if api_version is provided.
Expand All @@ -103,10 +103,10 @@ pub fn azure_policy_eval(
)?;
if let Some(ref api_ver) = api_version {
let mut req_ctx = Value::new_object();
let rc_map = req_ctx.as_object_mut()?;
rc_map.insert(Value::from("apiVersion"), Value::from(api_ver.clone()));
let ctx_map = context.as_object_mut()?;
ctx_map.insert(Value::from("requestContext"), req_ctx);
let mut rc_map = req_ctx.object_ref_mut()?;
rc_map.insert(Value::from("apiVersion"), Value::from(api_ver.clone()))?;
let mut ctx_map = context.object_ref_mut()?;
ctx_map.insert(Value::from("requestContext"), req_ctx)?;
}

// 5. Execute in the Rego VM.
Expand Down
10 changes: 5 additions & 5 deletions src/builtins/aggregates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fn max(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Resu
Value::Array(a) if a.is_empty() => Value::Undefined,
Value::Array(a) => a.iter().max().unwrap().clone(),
Value::Set(a) if a.is_empty() => Value::Undefined,
Value::Set(a) => a.iter().max().unwrap().clone(),
Value::Set(a) => a.as_ref().iter().max().unwrap().clone(),
a => {
let span = params[0].span();
bail!(span.error(format!("`max` requires array/set argument. Got `{a}`.").as_str()))
Expand All @@ -62,7 +62,7 @@ fn min(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Resu
Value::Array(a) if a.is_empty() => Value::Undefined,
Value::Array(a) => a.iter().min().unwrap().clone(),
Value::Set(a) if a.is_empty() => Value::Undefined,
Value::Set(a) => a.iter().min().unwrap().clone(),
Value::Set(a) => a.as_ref().iter().min().unwrap().clone(),
a => {
let span = params[0].span();
bail!(span.error(format!("`min` requires array/set argument. Got `{a}`.").as_str()))
Expand All @@ -76,7 +76,7 @@ fn product(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) ->
let mut v = Number::from(1_u64);
Ok(Value::from(match &args[0] {
Value::Array(a) => {
for e in a.iter() {
for e in a.as_ref().iter() {
v.mul_assign(&ensure_numeric("product", &params[0], e)?)?;
}
v
Expand Down Expand Up @@ -106,7 +106,7 @@ fn sort(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Res
// Sorting a set produces array.
Value::Set(a) => {
let mut items = Vec::with_capacity(a.len());
for value in a.iter() {
for value in a.as_ref().iter() {
items.push(value.clone());
// Guard array growth while materializing the sorted set.
enforce_limit()?;
Expand All @@ -126,7 +126,7 @@ fn sum(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Resu
let mut v = Number::from(0_u64);
Ok(Value::from(match &args[0] {
Value::Array(a) => {
for e in a.iter() {
for e in a.as_ref().iter() {
v.add_assign(&ensure_numeric("sum", &params[0], e)?)?;
}
v
Expand Down
18 changes: 10 additions & 8 deletions src/builtins/azure_policy/template_functions_collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,15 @@ fn fn_intersection(
// Intersection of objects: keep key-value pairs from the first
// object only when the key exists in every other object AND
// the value is equal across all of them.
let mut result: BTreeMap<Value, Value> = first.as_ref().clone();
let mut result: BTreeMap<Value, Value> =
first.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
for arg in rest {
let Value::Object(ref other) = *arg else {
return Ok(Value::Undefined);
};
result.retain(|k, v| other.get(k).is_some_and(|ov| *ov == *v));
}
Ok(Value::Object(Rc::new(result)))
Ok(Value::Object(Rc::new(result.into())))
}
_ => Ok(Value::Undefined),
}
Expand Down Expand Up @@ -130,7 +131,7 @@ fn fn_union(_span: &Span, _params: &[Ref<Expr>], args: &[Value], _strict: bool)
result.insert(k.clone(), merged);
}
}
Ok(Value::Object(Rc::new(result)))
Ok(Value::Object(Rc::new(result.into())))
}
_ => Ok(Value::Undefined),
}
Expand Down Expand Up @@ -273,24 +274,25 @@ fn fn_create_object(
}
}

Ok(Value::Object(Rc::new(map)))
Ok(Value::Object(Rc::new(map.into())))
}

// ── Helpers ───────────────────────────────────────────────────────────

/// Recursively merge two objects. Nested objects are merged; everything
/// else (including arrays) uses the value from `incoming`.
fn merge_objects(base: &BTreeMap<Value, Value>, overlay: &BTreeMap<Value, Value>) -> Value {
let mut result = base.clone();
for (k, v) in overlay {
fn merge_objects(base: &crate::collections::Object, overlay: &crate::collections::Object) -> Value {
let mut result: BTreeMap<Value, Value> =
base.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
for (k, v) in overlay.iter() {
#[allow(clippy::needless_borrowed_reference)]
let merged = match (result.get(k), v) {
(Some(&Value::Object(ref prev)), &Value::Object(ref next)) => merge_objects(prev, next),
_ => v.clone(),
};
result.insert(k.clone(), merged);
}
Value::Object(Rc::new(result))
Value::Object(Rc::new(result.into()))
}

fn extract_usize(v: &Value) -> Option<usize> {
Expand Down
4 changes: 2 additions & 2 deletions src/builtins/azure_policy/template_functions_misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ fn fn_items(_span: &Span, _params: &[Ref<Expr>], args: &[Value], _strict: bool)
return Ok(Value::Undefined);
};
let mut result = Vec::with_capacity(obj.len());
for (k, v) in obj.as_ref() {
for (k, v) in obj.iter() {
let mut entry = BTreeMap::<Value, Value>::new();
entry.insert(Value::from("key"), k.clone());
entry.insert(Value::from("value"), v.clone());
result.push(Value::Object(Rc::new(entry)));
result.push(Value::Object(Rc::new(entry.into())));
}
Ok(Value::Array(Rc::new(result)))
}
Expand Down
6 changes: 3 additions & 3 deletions src/builtins/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::lexer::Span;
use crate::value::Value;
use crate::*;

use alloc::collections::{BTreeMap, BTreeSet};
use alloc::collections::BTreeSet;

use anyhow::{bail, Result};

Expand Down Expand Up @@ -80,7 +80,7 @@ fn reachable(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) ->
}

fn visit(
graph: &BTreeMap<Value, Value>,
graph: &crate::collections::Object,
visited: &mut BTreeSet<Value>,
node: &Value,
path: &mut Vec<Value>,
Expand Down Expand Up @@ -127,7 +127,7 @@ fn visit(
arr.len()
}
Some(Value::Set(set)) => {
for n in set.iter().rev() {
for n in set.iter_sorted().rev() {
visit(graph, visited, n, path, paths)?;
}
set.len()
Expand Down
59 changes: 32 additions & 27 deletions src/builtins/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fn json_filter_impl(v: &Value, filter: &Value) -> Result<Value> {
Value::Object(fields) if fields.len() == 1 && filter[&Value::Null] == Value::Null => {
return Ok(v.clone())
}
Value::Object(fields) if !fields.is_empty() => fields,
Value::Object(fields) if !fields.is_empty() => fields.as_ref(),
_ => return Ok(v.clone()),
};

Expand All @@ -66,7 +66,7 @@ fn json_filter_impl(v: &Value, filter: &Value) -> Result<Value> {
Value::Set(s) => {
let mut items = BTreeSet::new();
for (item, filter) in filters.iter() {
if s.contains(item) {
if s.as_ref().contains(item) {
let item = json_filter_impl(item, filter)?;
if item != Value::Undefined {
items.insert(item);
Expand Down Expand Up @@ -130,7 +130,7 @@ fn json_remove_impl(v: &Value, filter: &Value) -> Result<Value> {

Value::Set(s) => {
let mut items = BTreeSet::new();
for item in s.iter() {
for item in s.as_ref().iter() {
if let Some(f) = filters.get(item) {
let v = json_remove_impl(item, f)?;
if v != Value::Undefined {
Expand All @@ -150,7 +150,7 @@ fn json_remove_impl(v: &Value, filter: &Value) -> Result<Value> {

Value::Object(obj) => {
let mut items = BTreeMap::new();
for (key, value) in obj.iter() {
for (key, value) in obj.as_ref().iter() {
if let Some(f) = filters.get(key) {
let v = json_remove_impl(value, f)?;
if v != Value::Undefined {
Expand Down Expand Up @@ -191,8 +191,9 @@ fn merge_filters(
// Guard recursive filter construction as path objects materialize.
enforce_limit()?;
}
if let Ok(f) = f.as_object_mut() {
f.insert(Value::Null, Value::Null);
if let Ok(mut f) = f.object_ref_mut() {
f.insert(Value::Null, Value::Null)
.map_err(|e| anyhow::anyhow!("{e}"))?;
// Guard filter map growth when marking terminal entries.
enforce_limit()?;
};
Expand All @@ -204,8 +205,10 @@ fn merge_filters(
for p in a.iter() {
let vref = match f {
Value::Object(obj) => {
let obj = Rc::make_mut(obj);
let entry = obj.entry(p.clone()).or_insert_with(Value::new_object);
let entry = Rc::make_mut(obj)
.entry(p.clone())
.map_err(|e| anyhow::anyhow!("{e}"))?
.or_insert_with(Value::new_object);
// Guard filter map growth when creating nested objects.
enforce_limit()?;
entry
Expand All @@ -216,8 +219,9 @@ fn merge_filters(
// Guard recursive descent as additional path components attach.
enforce_limit()?;
}
if let Ok(f) = f.as_object_mut() {
f.insert(Value::Null, Value::Null);
if let Ok(mut f) = f.object_ref_mut() {
f.insert(Value::Null, Value::Null)
.map_err(|e| anyhow::anyhow!("{e}"))?;
// Guard filter map growth when sealing terminal markers.
enforce_limit()?;
};
Expand Down Expand Up @@ -246,7 +250,7 @@ fn json_filter(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool)
_ => bail!(span.error(format!("`{name}` requires set/array argument").as_str())),
};

if let Ok(v) = filters.as_object() {
if let Ok(v) = filters.object_ref() {
if v.is_empty() {
return Ok(Value::new_object());
}
Expand All @@ -273,14 +277,14 @@ fn filter(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> R
let name = "object.filter";
ensure_args_count(span, name, params, args, 2)?;
let mut obj = ensure_object(name, &params[0], args[0].clone())?;
let obj_ref = Rc::make_mut(&mut obj);
let mut obj_ref = Rc::make_mut(&mut obj).as_mut();
match &args[1] {
Value::Array(a) => {
let keys: BTreeSet<&Value> = a.iter().collect();
obj_ref.retain(|k, _| keys.contains(k))
}
Value::Set(s) => obj_ref.retain(|k, _| s.contains(k)),
Value::Object(o) => obj_ref.retain(|k, _| o.contains_key(k)),
Value::Set(s) => obj_ref.retain(|k, _| s.as_ref().contains(k)),
Value::Object(o) => obj_ref.retain(|k, _| o.as_ref().contains_key(k)),
_ => bail!(span.error(format!("`{name}` requires array/object/set argument").as_str())),
};

Expand All @@ -305,7 +309,7 @@ fn get(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Resu
}
v.clone()
}
key => match obj.get(key) {
key => match obj.as_ref().get(key) {
Some(v) => v.clone(),
_ => default.clone(),
},
Expand All @@ -316,21 +320,21 @@ fn keys(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Res
let name = "object.keys";
ensure_args_count(span, name, params, args, 1)?;
let obj = ensure_object(name, &params[0], args[0].clone())?;
Ok(Value::from_set(obj.keys().cloned().collect()))
Ok(Value::from_set(obj.as_ref().keys().cloned().collect()))
}

fn remove(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "object.remove";
ensure_args_count(span, name, params, args, 2)?;
let mut obj = ensure_object(name, &params[0], args[0].clone())?;
let obj_ref = Rc::make_mut(&mut obj);
let mut obj_ref = Rc::make_mut(&mut obj).as_mut();
match &args[1] {
Value::Array(a) => {
let keys: BTreeSet<&Value> = a.iter().collect();
obj_ref.retain(|k, _| !keys.contains(k))
}
Value::Set(s) => obj_ref.retain(|k, _| !s.contains(k)),
Value::Object(o) => obj_ref.retain(|k, _| !o.contains_key(k)),
Value::Set(s) => obj_ref.retain(|k, _| !s.as_ref().contains(k)),
Value::Object(o) => obj_ref.retain(|k, _| !o.as_ref().contains_key(k)),
_ => bail!(span.error(format!("`{name}` requires array/object/set argument").as_str())),
};

Expand All @@ -340,15 +344,15 @@ fn remove(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> R
fn is_subset(sup: &Value, sub: &Value) -> bool {
match (sup, sub) {
(Value::Object(sup), Value::Object(sub)) => {
sub.iter().all(|(k, vsub)| {
match sup.get(k) {
sub.as_ref().iter().all(|(k, vsub)| {
match sup.as_ref().get(k) {
// Some(vsup @ Value::Object(_)) => is_subset(vsup, vsub),
Some(vsup) => is_subset(vsup, vsub),
_ => false,
}
})
}
(Value::Set(sup), Value::Set(sub)) => sub.is_subset(sup),
(Value::Set(sup), Value::Set(sub)) => sub.as_ref().is_subset(sup.as_ref()),
(Value::Array(sup), Value::Array(sub)) => sup.windows(sub.len()).any(|w| w == &sub[..]),
(Value::Array(sup), Value::Set(_)) => {
let sup = Value::from_set(sup.iter().cloned().collect());
Expand All @@ -369,14 +373,15 @@ fn union(obj1: &Value, obj2: &Value) -> Result<Value> {
match (obj1, obj2) {
(Value::Object(m1), Value::Object(m2)) => {
let mut u = obj1.clone();
let um = u.as_object_mut()?;
let mut um = u.object_ref_mut()?;

for (key2, value2) in m2.iter() {
let vm = match m1.get(key2) {
for (key2, value2) in m2.as_ref().iter() {
let vm = match m1.as_ref().get(key2) {
Some(value1) => union(value1, value2)?,
_ => value2.clone(),
};
um.insert(key2.clone(), vm);
um.insert(key2.clone(), vm)
.map_err(|e| anyhow::anyhow!("{e}"))?;
}
Ok(u)
}
Expand Down Expand Up @@ -407,7 +412,7 @@ fn object_union_n(

let mut u = Value::new_object();
for (idx, a) in arr.iter().enumerate() {
if a.as_object().is_err() {
if a.object_ref().is_err() {
if strict {
bail!(params[0]
.span()
Expand Down
Loading
Loading