use alloc::collections::{BTreeMap, BTreeSet};
use alloc::string::String;
use alloc::vec::Vec;
use crate::Value;
use super::super::obj_map::{make_value, new_map, obj_insert, val_str, ObjMap};
use super::super::types::ResolvedEntry;
use super::helpers::find_key_ci;
const ELEMENT_ENVELOPE_FIELDS: &[&str] = &["name", "type", "id", "etag"];
pub fn rewrap_sub_resource_arrays(
properties: &mut ObjMap,
sub_arrays: &BTreeSet<String>,
entries: &BTreeMap<String, ResolvedEntry>,
api_version: Option<&str>,
) {
let mut sorted: Vec<&String> = sub_arrays.iter().collect();
sorted.sort_by(|a, b| {
let depth_a = a.chars().filter(|&c| c == '.').count();
let depth_b = b.chars().filter(|&c| c == '.').count();
depth_b.cmp(&depth_a)
});
for sub_array_path in sorted {
let envelope_fields = classify_envelope_fields(sub_array_path, entries, api_version);
let parts: Vec<&str> = sub_array_path.split('.').collect();
if parts.len() == 1 {
if let Some(key) = parts.first().and_then(|p| find_key_ci(properties, p)) {
if let Some(Value::Array(arr)) = properties.get_mut(key.as_ref()) {
let inner = crate::Rc::make_mut(arr);
for elem in inner.iter_mut() {
*elem = rewrap_element(elem, &envelope_fields);
}
}
}
} else if let Some((&array_name, parent_parts)) = parts.split_last() {
rewrap_nested_array(properties, parent_parts, array_name, &envelope_fields);
}
}
}
fn classify_envelope_fields(
sub_array_path: &str,
entries: &BTreeMap<String, ResolvedEntry>,
api_version: Option<&str>,
) -> BTreeSet<String> {
let mut envelope = BTreeSet::new();
for &f in ELEMENT_ENVELOPE_FIELDS {
envelope.insert(f.to_ascii_lowercase());
}
let wildcard_prefix: String = {
let parts: Vec<&str> = sub_array_path.split('.').collect();
let mut prefix = String::new();
for (i, part) in parts.iter().enumerate() {
if i > 0 {
prefix.push_str("[*].");
}
prefix.push_str(&part.to_ascii_lowercase());
}
prefix.push_str("[*].");
prefix
};
for (lc_key, entry) in entries {
if !lc_key.starts_with(&wildcard_prefix) {
continue;
}
let field = &lc_key[wildcard_prefix.len()..];
let first_segment = field.split('.').next().unwrap_or(field);
let arm_path = entry.select_path(api_version);
let arm_after_last_wildcard = arm_path.rsplit("[*].").next().unwrap_or("");
if !arm_after_last_wildcard.starts_with("properties.") {
envelope.insert(first_segment.to_ascii_lowercase());
}
}
envelope
}
fn rewrap_nested_array(
obj: &mut ObjMap,
parent_parts: &[&str],
array_name: &str,
envelope_fields: &BTreeSet<String>,
) {
if parent_parts.is_empty() {
return;
}
let parent_key = match parent_parts.first().and_then(|&p| find_key_ci(obj, p)) {
Some(k) => k,
None => return,
};
let parent_arr = match obj.get_mut(parent_key.as_ref()) {
Some(Value::Array(arr)) => crate::Rc::make_mut(arr),
_ => return,
};
for element in parent_arr.iter_mut() {
if let Value::Object(obj_rc) = element {
let inner_btree = crate::Rc::make_mut(obj_rc);
if parent_parts.len() > 1 {
rewrap_nested_array_in_btree(
inner_btree,
parent_parts.get(1..).unwrap_or_default(),
array_name,
envelope_fields,
);
} else if let Some(arr_key) = find_key_ci_btree(inner_btree, array_name) {
if let Some(Value::Array(arr)) = inner_btree.get_mut(&arr_key) {
let inner = crate::Rc::make_mut(arr);
for inner_elem in inner.iter_mut() {
*inner_elem = rewrap_element(inner_elem, envelope_fields);
}
}
}
}
}
}
fn rewrap_nested_array_in_btree(
btree: &mut alloc::collections::BTreeMap<Value, Value>,
parent_parts: &[&str],
array_name: &str,
envelope_fields: &BTreeSet<String>,
) {
if parent_parts.is_empty() {
return;
}
let parent_key = match parent_parts
.first()
.and_then(|&p| find_key_ci_btree(btree, p))
{
Some(k) => k,
None => return,
};
let parent_arr = match btree.get_mut(&parent_key) {
Some(Value::Array(arr)) => crate::Rc::make_mut(arr),
_ => return,
};
for element in parent_arr.iter_mut() {
if let Value::Object(obj_rc) = element {
let inner_btree = crate::Rc::make_mut(obj_rc);
if parent_parts.len() > 1 {
rewrap_nested_array_in_btree(
inner_btree,
parent_parts.get(1..).unwrap_or_default(),
array_name,
envelope_fields,
);
} else if let Some(arr_key) = find_key_ci_btree(inner_btree, array_name) {
if let Some(Value::Array(arr)) = inner_btree.get_mut(&arr_key) {
let inner = crate::Rc::make_mut(arr);
for inner_elem in inner.iter_mut() {
*inner_elem = rewrap_element(inner_elem, envelope_fields);
}
}
}
}
}
}
fn find_key_ci_btree(
btree: &alloc::collections::BTreeMap<Value, Value>,
key: &str,
) -> Option<Value> {
btree
.keys()
.find(|k| val_str(k).is_some_and(|s| s.eq_ignore_ascii_case(key)))
.cloned()
}
fn rewrap_element(element: &Value, envelope_fields: &BTreeSet<String>) -> Value {
let obj = match element.as_object() {
Ok(o) => o,
Err(_) => return element.clone(),
};
let mut envelope = new_map();
let mut props = new_map();
for (key, val) in obj.iter() {
let key_s = match val_str(key) {
Some(s) => s,
None => continue,
};
if envelope_fields.contains(&key_s.to_ascii_lowercase()) {
obj_insert(&mut envelope, key_s, val.clone());
} else {
obj_insert(&mut props, key_s, val.clone());
}
}
if !props.is_empty() {
obj_insert(&mut envelope, "properties", make_value(props));
}
make_value(envelope)
}