use serde_json::{Map, Value};
pub(crate) fn insert_path(root: &mut Value, segments: &[&str], value: Value) -> Result<(), String> {
if segments.is_empty() {
return Err("configuration path cannot be empty".to_owned());
}
insert_path_recursive(root, segments, value)
}
fn insert_path_recursive(
current: &mut Value,
segments: &[&str],
value: Value,
) -> Result<(), String> {
let segment = segments[0];
if segment.is_empty() {
return Err("configuration path contains an empty segment".to_owned());
}
let is_last = segments.len() == 1;
match current {
Value::Object(map) => {
if is_last {
map.insert(segment.to_owned(), value);
return Ok(());
}
let next_is_index = segments[1].parse::<usize>().is_ok();
let child = map.entry(segment.to_owned()).or_insert_with(|| {
if next_is_index {
Value::Array(Vec::new())
} else {
Value::Object(Map::new())
}
});
match child {
Value::Object(_) if !next_is_index => {}
Value::Array(_) if next_is_index => {}
_ => {
return Err(format!(
"path segment {segment} conflicts with an existing non-container value"
));
}
}
insert_path_recursive(child, &segments[1..], value)
}
Value::Array(values) => {
let index = segment.parse::<usize>().map_err(|_| {
format!("path segment {segment} must be an array index at this position")
})?;
if is_last {
if values.len() <= index {
values.resize(index + 1, Value::Null);
}
values[index] = value;
return Ok(());
}
let next_is_index = segments[1].parse::<usize>().is_ok();
if values.len() <= index {
values.resize_with(index + 1, || {
if next_is_index {
Value::Array(Vec::new())
} else {
Value::Object(Map::new())
}
});
}
let child = &mut values[index];
if child.is_null() {
*child = if next_is_index {
Value::Array(Vec::new())
} else {
Value::Object(Map::new())
};
}
match child {
Value::Object(_) if !next_is_index => {}
Value::Array(_) if next_is_index => {}
_ => {
return Err(format!(
"path segment {segment} conflicts with an existing non-container value"
));
}
}
insert_path_recursive(child, &segments[1..], value)
}
_ => Err(format!(
"path segment {segment} conflicts with an existing non-container value"
)),
}
}