use serde_json::Value;
use crate::error::AgentConfigError;
use super::common::{ensure_array, prune_empty_path, traverse_array, traverse_array_mut};
#[allow(dead_code)]
pub(crate) fn upsert_named_array_entry(
root: &mut Value,
path: &[&str],
name_field: &str,
name: &str,
value: Value,
) -> Result<bool, AgentConfigError> {
let arr = ensure_array(root, path)?;
let idx = arr.iter().position(|v| {
v.as_object()
.and_then(|o| o.get(name_field))
.and_then(Value::as_str)
== Some(name)
});
match idx {
Some(i) if arr[i] == value => Ok(false),
Some(i) => {
arr[i] = value;
Ok(true)
}
None => {
arr.push(value);
Ok(true)
}
}
}
#[allow(dead_code)]
pub(crate) fn remove_named_array_entry(
root: &mut Value,
path: &[&str],
name_field: &str,
name: &str,
) -> Result<bool, AgentConfigError> {
let Some(arr) = traverse_array_mut(root, path) else {
return Ok(false);
};
let Some(idx) = arr.iter().position(|v| {
v.as_object()
.and_then(|o| o.get(name_field))
.and_then(Value::as_str)
== Some(name)
}) else {
return Ok(false);
};
arr.remove(idx);
prune_empty_path(root, path);
Ok(true)
}
#[allow(dead_code)]
pub(crate) fn contains_in_named_array(
root: &Value,
path: &[&str],
name_field: &str,
name: &str,
) -> bool {
let Some(arr) = traverse_array(root, path) else {
return false;
};
arr.iter().any(|v| {
v.as_object()
.and_then(|o| o.get(name_field))
.and_then(Value::as_str)
== Some(name)
})
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn upsert_named_array_entry_round_trip() {
let mut root = json!({});
let changed = upsert_named_array_entry(
&mut root,
&["mcp"],
"name",
"github",
json!({ "name": "github", "command": "npx" }),
)
.unwrap();
assert!(changed);
assert_eq!(
root,
json!({ "mcp": [ { "name": "github", "command": "npx" } ] })
);
let unchanged = upsert_named_array_entry(
&mut root,
&["mcp"],
"name",
"github",
json!({ "name": "github", "command": "npx" }),
)
.unwrap();
assert!(!unchanged);
let changed = upsert_named_array_entry(
&mut root,
&["mcp"],
"name",
"github",
json!({ "name": "github", "command": "node" }),
)
.unwrap();
assert!(changed);
let arr = root["mcp"].as_array().unwrap();
assert_eq!(arr.len(), 1);
assert_eq!(arr[0]["command"], json!("node"));
}
#[test]
fn remove_named_array_entry_keeps_other_entries() {
let mut root = json!({
"mcp": [
{ "name": "alpha" },
{ "name": "beta" }
]
});
assert!(remove_named_array_entry(&mut root, &["mcp"], "name", "alpha").unwrap());
assert_eq!(root, json!({ "mcp": [ { "name": "beta" } ] }));
}
#[test]
fn remove_named_array_entry_prunes_empty_array() {
let mut root = json!({ "mcp": [ { "name": "alpha" } ] });
assert!(remove_named_array_entry(&mut root, &["mcp"], "name", "alpha").unwrap());
assert_eq!(root, json!({}));
}
#[test]
fn contains_in_named_array_finds_entry() {
let root = json!({ "mcp": [ { "name": "alpha" } ] });
assert!(contains_in_named_array(&root, &["mcp"], "name", "alpha"));
assert!(!contains_in_named_array(&root, &["mcp"], "name", "ghost"));
}
}