use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum ToolPermission {
#[default]
Allow,
Ask,
Deny,
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct PermissionConfig {
pub defaults: BTreeMap<String, ToolPermission>,
pub agents: BTreeMap<String, BTreeMap<String, ToolPermission>>,
}
impl<'de> Deserialize<'de> for PermissionConfig {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let raw: BTreeMap<String, toml::Value> = BTreeMap::deserialize(deserializer)?;
let mut defaults = BTreeMap::new();
let mut agents = BTreeMap::new();
for (key, value) in raw {
match value {
toml::Value::String(s) => {
let perm: ToolPermission =
serde::Deserialize::deserialize(toml::Value::String(s))
.map_err(serde::de::Error::custom)?;
defaults.insert(key, perm);
}
toml::Value::Table(table) => {
let agent_perms: BTreeMap<String, ToolPermission> = table
.into_iter()
.map(|(k, v)| {
let perm: ToolPermission = serde::Deserialize::deserialize(v)
.map_err(|e| format!("permissions.{key}.{k}: {e}"))
.unwrap_or_default();
(k, perm)
})
.collect();
agents.insert(key, agent_perms);
}
_ => {}
}
}
Ok(PermissionConfig { defaults, agents })
}
}
impl PermissionConfig {
pub fn resolve(&self, agent: &str, tool: &str) -> ToolPermission {
if let Some(agent_perms) = self.agents.get(agent)
&& let Some(&perm) = agent_perms.get(tool)
{
return perm;
}
self.defaults.get(tool).copied().unwrap_or_default()
}
}