use std::collections::{BTreeMap, BTreeSet};
use helm_schema_core::ContractPathSchemaEvidence;
use serde_json::Value;
pub fn apply_required_inference(
schema: &mut Value,
schema_evidence_by_value_path: &BTreeMap<String, ContractPathSchemaEvidence>,
explicit_default_value_paths: &BTreeSet<String>,
) {
for (path, evidence) in schema_evidence_by_value_path {
if !evidence.is_required_inference_candidate()
|| explicit_default_value_paths.contains(path)
{
continue;
}
add_path_to_required(schema, path);
}
}
fn add_path_to_required(schema: &mut Value, vp: &str) {
let parts = crate::split_value_path(vp);
let Some((leaf, parents)) = parts.split_last() else {
return;
};
let Some(parent) = navigate_to_object_property(schema, parents) else {
return;
};
add_to_required_list(parent, leaf);
}
fn navigate_to_object_property<'a>(
schema: &'a mut Value,
segments: &[String],
) -> Option<&'a mut Value> {
let mut node = schema;
for seg in segments {
node = node
.as_object_mut()?
.get_mut("properties")?
.as_object_mut()?
.get_mut(seg.as_str())?;
}
Some(node)
}
fn add_to_required_list(node: &mut Value, key: &str) {
let Some(obj) = node.as_object_mut() else {
return;
};
let req = obj
.entry("required".to_string())
.or_insert_with(|| Value::Array(Vec::new()));
let Some(arr) = req.as_array_mut() else {
return;
};
if !arr.iter().any(|v| v.as_str() == Some(key)) {
arr.push(Value::String(key.to_string()));
}
arr.sort_by(|a, b| a.as_str().unwrap_or("").cmp(b.as_str().unwrap_or("")));
arr.dedup();
}
#[cfg(test)]
#[path = "tests/required_inference.rs"]
mod tests;