1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
9#[serde(rename_all = "lowercase")]
10pub enum PermissionAction {
11 Allow,
12 Deny,
13 Ask,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18#[serde(rename_all = "camelCase")]
19pub struct PermissionRule {
20 pub permission: String,
22 pub pattern: String,
24 pub action: PermissionAction,
26}
27
28pub type Ruleset = Vec<PermissionRule>;
30
31#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
33#[serde(rename_all = "camelCase")]
34pub struct PermissionToolRef {
35 pub message_id: String,
37 pub call_id: String,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43#[serde(rename_all = "camelCase")]
44pub struct PermissionRequest {
45 pub id: String,
47 #[serde(default, alias = "sessionID")]
49 pub session_id: Option<String>,
50 pub permission: String,
52 pub patterns: Vec<String>,
54 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub metadata: Option<serde_json::Value>,
57 #[serde(default)]
59 pub always: Vec<String>,
60 #[serde(default, skip_serializing_if = "Option::is_none")]
62 pub tool: Option<PermissionToolRef>,
63}
64
65#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
67#[serde(rename_all = "lowercase")]
68pub enum PermissionReply {
69 Once,
71 Always,
73 Reject,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79#[serde(rename_all = "camelCase")]
80pub struct PermissionReplyRequest {
81 pub reply: PermissionReply,
83 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub message: Option<String>,
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_permission_action_serialize() {
94 assert_eq!(
95 serde_json::to_string(&PermissionAction::Allow).unwrap(),
96 r#""allow""#
97 );
98 assert_eq!(
99 serde_json::to_string(&PermissionAction::Deny).unwrap(),
100 r#""deny""#
101 );
102 assert_eq!(
103 serde_json::to_string(&PermissionAction::Ask).unwrap(),
104 r#""ask""#
105 );
106 }
107
108 #[test]
109 fn test_permission_rule_deserialize() {
110 let json = r#"{"permission":"file.read","pattern":"**/*.rs","action":"allow"}"#;
111 let rule: PermissionRule = serde_json::from_str(json).unwrap();
112 assert_eq!(rule.permission, "file.read");
113 assert_eq!(rule.pattern, "**/*.rs");
114 assert_eq!(rule.action, PermissionAction::Allow);
115 }
116
117 #[test]
118 fn test_ruleset_deserialize() {
119 let json = r#"[
120 {"permission":"file.read","pattern":"**/*.rs","action":"allow"},
121 {"permission":"bash.execute","pattern":"*","action":"ask"}
122 ]"#;
123 let ruleset: Ruleset = serde_json::from_str(json).unwrap();
124 assert_eq!(ruleset.len(), 2);
125 assert_eq!(ruleset[0].action, PermissionAction::Allow);
126 assert_eq!(ruleset[1].action, PermissionAction::Ask);
127 }
128
129 #[test]
130 fn test_permission_request_deserialize() {
131 let json = r#"{
132 "id": "req-123",
133 "sessionId": "sess-456",
134 "permission": "file.write",
135 "patterns": ["src/*.rs", "lib/*.rs"],
136 "always": ["src/*.rs"],
137 "tool": {"messageId": "msg-1", "callId": "call-1"}
138 }"#;
139 let req: PermissionRequest = serde_json::from_str(json).unwrap();
140 assert_eq!(req.id, "req-123");
141 assert_eq!(req.session_id.as_deref(), Some("sess-456"));
142 assert_eq!(req.permission, "file.write");
143 assert_eq!(req.patterns.len(), 2);
144 assert_eq!(req.always.len(), 1);
145 assert!(req.tool.is_some());
146 let tool = req.tool.unwrap();
147 assert_eq!(tool.message_id, "msg-1");
148 assert_eq!(tool.call_id, "call-1");
149 }
150
151 #[test]
152 fn test_permission_request_minimal() {
153 let json = r#"{
154 "id": "req-123",
155 "sessionId": "sess-456",
156 "permission": "file.read",
157 "patterns": ["**/*"]
158 }"#;
159 let req: PermissionRequest = serde_json::from_str(json).unwrap();
160 assert_eq!(req.id, "req-123");
161 assert!(req.always.is_empty());
162 assert!(req.tool.is_none());
163 assert!(req.metadata.is_none());
164 }
165
166 #[test]
167 fn test_permission_request_session_id_alias() {
168 let json = r#"{
169 "id": "req-123",
170 "sessionID": "sess-456",
171 "permission": "file.read",
172 "patterns": ["**/*"]
173 }"#;
174 let req: PermissionRequest = serde_json::from_str(json).unwrap();
175 assert_eq!(req.session_id.as_deref(), Some("sess-456"));
176 }
177
178 #[test]
179 fn test_permission_reply_serialize() {
180 assert_eq!(
181 serde_json::to_string(&PermissionReply::Once).unwrap(),
182 r#""once""#
183 );
184 assert_eq!(
185 serde_json::to_string(&PermissionReply::Always).unwrap(),
186 r#""always""#
187 );
188 assert_eq!(
189 serde_json::to_string(&PermissionReply::Reject).unwrap(),
190 r#""reject""#
191 );
192 }
193
194 #[test]
195 fn test_permission_reply_request_serialize() {
196 let req = PermissionReplyRequest {
197 reply: PermissionReply::Always,
198 message: Some("User approved".to_string()),
199 };
200 let json = serde_json::to_string(&req).unwrap();
201 assert!(json.contains(r#""reply":"always""#));
202 assert!(json.contains(r#""message":"User approved""#));
203 }
204
205 #[test]
206 fn test_permission_reply_request_minimal() {
207 let json = r#"{"reply":"once"}"#;
208 let req: PermissionReplyRequest = serde_json::from_str(json).unwrap();
209 assert_eq!(req.reply, PermissionReply::Once);
210 assert!(req.message.is_none());
211 }
212}