use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum PermissionAction {
Allow,
Deny,
Ask,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PermissionRule {
pub permission: String,
pub pattern: String,
pub action: PermissionAction,
}
pub type Ruleset = Vec<PermissionRule>;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PermissionToolRef {
pub message_id: String,
pub call_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PermissionRequest {
pub id: String,
#[serde(default, alias = "sessionID")]
pub session_id: Option<String>,
pub permission: String,
pub patterns: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<serde_json::Value>,
#[serde(default)]
pub always: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool: Option<PermissionToolRef>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum PermissionReply {
Once,
Always,
Reject,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PermissionReplyRequest {
pub reply: PermissionReply,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_permission_action_serialize() {
assert_eq!(
serde_json::to_string(&PermissionAction::Allow).unwrap(),
r#""allow""#
);
assert_eq!(
serde_json::to_string(&PermissionAction::Deny).unwrap(),
r#""deny""#
);
assert_eq!(
serde_json::to_string(&PermissionAction::Ask).unwrap(),
r#""ask""#
);
}
#[test]
fn test_permission_rule_deserialize() {
let json = r#"{"permission":"file.read","pattern":"**/*.rs","action":"allow"}"#;
let rule: PermissionRule = serde_json::from_str(json).unwrap();
assert_eq!(rule.permission, "file.read");
assert_eq!(rule.pattern, "**/*.rs");
assert_eq!(rule.action, PermissionAction::Allow);
}
#[test]
fn test_ruleset_deserialize() {
let json = r#"[
{"permission":"file.read","pattern":"**/*.rs","action":"allow"},
{"permission":"bash.execute","pattern":"*","action":"ask"}
]"#;
let ruleset: Ruleset = serde_json::from_str(json).unwrap();
assert_eq!(ruleset.len(), 2);
assert_eq!(ruleset[0].action, PermissionAction::Allow);
assert_eq!(ruleset[1].action, PermissionAction::Ask);
}
#[test]
fn test_permission_request_deserialize() {
let json = r#"{
"id": "req-123",
"sessionId": "sess-456",
"permission": "file.write",
"patterns": ["src/*.rs", "lib/*.rs"],
"always": ["src/*.rs"],
"tool": {"messageId": "msg-1", "callId": "call-1"}
}"#;
let req: PermissionRequest = serde_json::from_str(json).unwrap();
assert_eq!(req.id, "req-123");
assert_eq!(req.session_id.as_deref(), Some("sess-456"));
assert_eq!(req.permission, "file.write");
assert_eq!(req.patterns.len(), 2);
assert_eq!(req.always.len(), 1);
assert!(req.tool.is_some());
let tool = req.tool.unwrap();
assert_eq!(tool.message_id, "msg-1");
assert_eq!(tool.call_id, "call-1");
}
#[test]
fn test_permission_request_minimal() {
let json = r#"{
"id": "req-123",
"sessionId": "sess-456",
"permission": "file.read",
"patterns": ["**/*"]
}"#;
let req: PermissionRequest = serde_json::from_str(json).unwrap();
assert_eq!(req.id, "req-123");
assert!(req.always.is_empty());
assert!(req.tool.is_none());
assert!(req.metadata.is_none());
}
#[test]
fn test_permission_request_session_id_alias() {
let json = r#"{
"id": "req-123",
"sessionID": "sess-456",
"permission": "file.read",
"patterns": ["**/*"]
}"#;
let req: PermissionRequest = serde_json::from_str(json).unwrap();
assert_eq!(req.session_id.as_deref(), Some("sess-456"));
}
#[test]
fn test_permission_reply_serialize() {
assert_eq!(
serde_json::to_string(&PermissionReply::Once).unwrap(),
r#""once""#
);
assert_eq!(
serde_json::to_string(&PermissionReply::Always).unwrap(),
r#""always""#
);
assert_eq!(
serde_json::to_string(&PermissionReply::Reject).unwrap(),
r#""reject""#
);
}
#[test]
fn test_permission_reply_request_serialize() {
let req = PermissionReplyRequest {
reply: PermissionReply::Always,
message: Some("User approved".to_string()),
};
let json = serde_json::to_string(&req).unwrap();
assert!(json.contains(r#""reply":"always""#));
assert!(json.contains(r#""message":"User approved""#));
}
#[test]
fn test_permission_reply_request_minimal() {
let json = r#"{"reply":"once"}"#;
let req: PermissionReplyRequest = serde_json::from_str(json).unwrap();
assert_eq!(req.reply, PermissionReply::Once);
assert!(req.message.is_none());
}
}