mod alias_resolution;
mod element_remap;
mod flatten;
pub(crate) use element_remap::{apply_element_remap, ElementRemap};
use crate::Value;
use super::obj_map::{
extract_type_field, make_value, new_map, obj_contains, obj_insert, val_str, ObjMap, ROOT_FIELDS,
};
use super::types::ResolvedAliases;
use super::AliasRegistry;
use flatten::{lowercase_object_keys, normalize_value};
pub fn normalize(
arm_resource: &Value,
registry: Option<&AliasRegistry>,
api_version: Option<&str>,
) -> Value {
let aliases = registry.and_then(|r| extract_type_field(arm_resource).and_then(|rt| r.get(rt)));
normalize_with_aliases(arm_resource, aliases, api_version)
}
pub fn normalize_with_aliases(
arm_resource: &Value,
aliases: Option<&ResolvedAliases>,
api_version: Option<&str>,
) -> Value {
let obj = match arm_resource.as_object() {
Ok(o) => o,
Err(_) => return arm_resource.clone(),
};
let sub_arrays_ref = aliases.map(|a| &a.sub_resource_arrays);
let mut result = new_map();
let is_data_plane =
extract_type_field(arm_resource).is_some_and(|t| t.to_ascii_lowercase().contains(".data/"));
if is_data_plane {
for (key, val) in obj {
let key_s = match val_str(key) {
Some(s) => s,
None => continue,
};
if key_s.eq_ignore_ascii_case("properties") {
continue;
}
let lc_key = key_s.to_ascii_lowercase();
let val =
if key_s.eq_ignore_ascii_case("tags") || key_s.eq_ignore_ascii_case("identity") {
lowercase_object_keys(val)
} else {
normalize_value(val, key_s, sub_arrays_ref)
};
obj_insert(&mut result, &lc_key, val);
}
merge_properties(obj, &mut result, sub_arrays_ref);
} else {
for &field in ROOT_FIELDS {
let found = obj
.iter()
.find(|(k, _)| val_str(k).is_some_and(|s| s.eq_ignore_ascii_case(field)))
.map(|(_, v)| v);
if let Some(val) = found {
let val = if field.eq_ignore_ascii_case("tags")
|| field.eq_ignore_ascii_case("identity")
{
lowercase_object_keys(val)
} else {
normalize_value(val, field, sub_arrays_ref)
};
obj_insert(&mut result, &field.to_ascii_lowercase(), val);
}
}
merge_properties(obj, &mut result, sub_arrays_ref);
}
if let Some(aliases) = aliases {
alias_resolution::apply_alias_entries(&mut result, arm_resource, aliases, api_version);
}
make_value(result)
}
fn merge_properties(
obj: &alloc::collections::BTreeMap<Value, Value>,
result: &mut ObjMap,
sub_arrays: Option<&alloc::collections::BTreeSet<alloc::string::String>>,
) {
let props_val = obj
.iter()
.find(|(k, _)| val_str(k).is_some_and(|s| s.eq_ignore_ascii_case("properties")))
.map(|(_, v)| v);
if let Some(Value::Object(props)) = props_val {
for (key, val) in props.iter() {
let key_s = match val_str(key) {
Some(s) => s,
None => continue,
};
let lc_key = key_s.to_ascii_lowercase();
if obj_contains(result, &lc_key) {
continue;
}
let normalized = normalize_value(val, key_s, sub_arrays);
obj_insert(result, &lc_key, normalized);
}
}
}
pub fn build_input_envelope(
normalized_resource: Value,
context: Option<Value>,
parameters: Option<Value>,
) -> Value {
let mut envelope = new_map();
obj_insert(&mut envelope, "resource", normalized_resource);
obj_insert(
&mut envelope,
"context",
context.unwrap_or_else(|| make_value(new_map())),
);
obj_insert(
&mut envelope,
"parameters",
parameters.unwrap_or_else(|| make_value(new_map())),
);
make_value(envelope)
}