use serde_json::{Map, Value};
pub(crate) fn value_at_path<'a>(value: &'a Value, path: &[&str]) -> Option<&'a Value> {
let mut current = value;
for key in path {
current = current.as_object()?.get(*key)?;
}
Some(current)
}
pub(crate) fn set_path(value: &mut Value, path: &[&str], next: Value) -> anyhow::Result<()> {
if path.is_empty() {
*value = next;
return Ok(());
}
let mut current = value;
for key in &path[..path.len() - 1] {
let object = current
.as_object_mut()
.ok_or_else(|| anyhow::anyhow!("settings path parent is not an object"))?;
current = object
.entry((*key).to_string())
.or_insert_with(|| Value::Object(Map::new()));
if !current.is_object() {
*current = Value::Object(Map::new());
}
}
current
.as_object_mut()
.ok_or_else(|| anyhow::anyhow!("settings path parent is not an object"))?
.insert(path[path.len() - 1].to_string(), next);
Ok(())
}
pub(crate) fn remove_path(value: &mut Value, path: &[&str]) {
if path.is_empty() {
return;
}
let mut current = value;
for key in &path[..path.len() - 1] {
let Some(next) = current
.as_object_mut()
.and_then(|object| object.get_mut(*key))
else {
return;
};
current = next;
}
if let Some(object) = current.as_object_mut() {
object.remove(path[path.len() - 1]);
}
}
pub(crate) fn value_at_path_mut_or_create<'a>(
value: &'a mut Value,
path: &[&str],
) -> anyhow::Result<&'a mut Map<String, Value>> {
let mut current = value;
for key in path {
let object = current
.as_object_mut()
.ok_or_else(|| anyhow::anyhow!("settings path parent is not an object"))?;
current = object
.entry((*key).to_string())
.or_insert_with(|| Value::Object(Map::new()));
if !current.is_object() {
*current = Value::Object(Map::new());
}
}
current
.as_object_mut()
.ok_or_else(|| anyhow::anyhow!("settings path is not an object"))
}
pub(crate) fn has_path(root: Option<&Map<String, Value>>, path: &[&str]) -> bool {
let Some(mut current) = root.and_then(|object| path.first().and_then(|key| object.get(*key)))
else {
return path.is_empty() && root.is_some();
};
for key in &path[1..] {
let Some(next) = current.as_object().and_then(|object| object.get(*key)) else {
return false;
};
current = next;
}
true
}