use std::fmt;
use std::str::FromStr;
use anyhow::{Result, anyhow};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub(crate) enum Scope {
ContextRead,
ToolExecute,
CliFull,
Admin,
}
impl Scope {
pub(crate) fn grants(&self, required: &Scope) -> bool {
matches!(self, Scope::Admin) || self == required
}
pub(crate) fn as_str(&self) -> &'static str {
match self {
Scope::ContextRead => "context-read",
Scope::ToolExecute => "tool-execute",
Scope::CliFull => "cli-full",
Scope::Admin => "admin",
}
}
}
impl fmt::Display for Scope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for Scope {
type Err = anyhow::Error;
fn from_str(value: &str) -> Result<Self> {
match value.trim().to_ascii_lowercase().as_str() {
"context-read" | "context_read" | "contextread" | "read" => Ok(Scope::ContextRead),
"tool-execute" | "tool_execute" | "toolexecute" | "write" => Ok(Scope::ToolExecute),
"cli-full" | "cli_full" | "clifull" => Ok(Scope::CliFull),
"admin" => Ok(Scope::Admin),
other => Err(anyhow!(
"Unknown scope '{}'. Use: context-read, tool-execute, cli-full, admin",
other
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scope_display_and_parse() {
assert_eq!(Scope::ContextRead.to_string(), "context-read");
assert_eq!(Scope::ToolExecute.to_string(), "tool-execute");
assert_eq!(Scope::CliFull.to_string(), "cli-full");
assert_eq!(Scope::Admin.to_string(), "admin");
assert_eq!("context-read".parse::<Scope>().unwrap(), Scope::ContextRead);
assert_eq!("READ".parse::<Scope>().unwrap(), Scope::ContextRead);
assert_eq!("write".parse::<Scope>().unwrap(), Scope::ToolExecute);
assert_eq!("cli_full".parse::<Scope>().unwrap(), Scope::CliFull);
assert_eq!("Admin".parse::<Scope>().unwrap(), Scope::Admin);
assert!("invalid".parse::<Scope>().is_err());
}
#[test]
fn admin_grants_every_scope() {
assert!(Scope::Admin.grants(&Scope::ContextRead));
assert!(Scope::Admin.grants(&Scope::ToolExecute));
assert!(Scope::Admin.grants(&Scope::CliFull));
assert!(Scope::Admin.grants(&Scope::Admin));
assert!(!Scope::ContextRead.grants(&Scope::ToolExecute));
}
}