magi-code 0.77.1

Repository-aware CLI coding agent for terminal work
Documentation
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
}