sacp/schema/
enum_impls.rs

1//! JrRequest and JrNotification implementations for ACP enum types.
2//!
3//! This module implements the JSON-RPC traits for the enum types from
4//! agent-client-protocol-schema that represent all possible messages:
5//! - ClientRequest/AgentResponse (messages agents receive/send)
6//! - ClientNotification (notifications agents receive)
7//! - AgentRequest/ClientResponse (messages clients receive/send)
8//! - AgentNotification (notifications clients receive)
9
10use crate::schema::{AgentNotification, AgentRequest, ClientNotification, ClientRequest};
11use serde::Serialize;
12
13use crate::jsonrpc::{JrMessage, JrNotification, JrRequest};
14use crate::util::json_cast;
15
16// ============================================================================
17// Agent side (messages that agents receive)
18// ============================================================================
19
20impl JrMessage for ClientRequest {
21    fn method(&self) -> &str {
22        match self {
23            ClientRequest::InitializeRequest(_) => "initialize",
24            ClientRequest::AuthenticateRequest(_) => "authenticate",
25            ClientRequest::NewSessionRequest(_) => "session/new",
26            ClientRequest::LoadSessionRequest(_) => "session/load",
27            ClientRequest::SetSessionModeRequest(_) => "session/set_mode",
28            ClientRequest::PromptRequest(_) => "session/prompt",
29            ClientRequest::ExtMethodRequest(ext) => &ext.method,
30        }
31    }
32
33    fn to_untyped_message(&self) -> Result<crate::UntypedMessage, crate::Error> {
34        crate::UntypedMessage::new(self.method(), self)
35    }
36
37    fn parse_message(method: &str, params: &impl Serialize) -> Option<Result<Self, crate::Error>> {
38        let result = match method {
39            "initialize" => json_cast(params).map(ClientRequest::InitializeRequest),
40            "authenticate" => json_cast(params).map(ClientRequest::AuthenticateRequest),
41            "session/new" => json_cast(params).map(ClientRequest::NewSessionRequest),
42            "session/load" => json_cast(params).map(ClientRequest::LoadSessionRequest),
43            "session/set_mode" => json_cast(params).map(ClientRequest::SetSessionModeRequest),
44            "session/prompt" => json_cast(params).map(ClientRequest::PromptRequest),
45            _ => {
46                // Check for extension methods (prefixed with underscore)
47                if let Some(custom_method) = method.strip_prefix('_') {
48                    json_cast(params).map(|ext_req: crate::schema::ExtRequest| {
49                        ClientRequest::ExtMethodRequest(crate::schema::ExtRequest {
50                            method: custom_method.to_string().into(),
51                            params: ext_req.params,
52                        })
53                    })
54                } else {
55                    return None;
56                }
57            }
58        };
59
60        Some(result)
61    }
62}
63
64impl JrRequest for ClientRequest {
65    type Response = serde_json::Value;
66}
67
68impl JrMessage for ClientNotification {
69    fn method(&self) -> &str {
70        match self {
71            ClientNotification::CancelNotification(_) => "session/cancel",
72            ClientNotification::ExtNotification(ext) => &ext.method,
73        }
74    }
75
76    fn to_untyped_message(&self) -> Result<crate::UntypedMessage, crate::Error> {
77        crate::UntypedMessage::new(self.method(), self)
78    }
79
80    fn parse_message(method: &str, params: &impl Serialize) -> Option<Result<Self, crate::Error>> {
81        let result = match method {
82            "session/cancel" => json_cast(params).map(ClientNotification::CancelNotification),
83            _ => {
84                // Check for extension notifications (prefixed with underscore)
85                if let Some(custom_method) = method.strip_prefix('_') {
86                    json_cast(params).map(|ext_notif: crate::schema::ExtNotification| {
87                        ClientNotification::ExtNotification(crate::schema::ExtNotification {
88                            method: custom_method.to_string().into(),
89                            params: ext_notif.params,
90                        })
91                    })
92                } else {
93                    return None;
94                }
95            }
96        };
97
98        Some(result)
99    }
100}
101
102impl JrNotification for ClientNotification {}
103
104// ============================================================================
105// Client side (messages that clients/editors receive)
106// ============================================================================
107
108impl JrMessage for AgentRequest {
109    fn method(&self) -> &str {
110        match self {
111            AgentRequest::WriteTextFileRequest(_) => "fs/write_text_file",
112            AgentRequest::ReadTextFileRequest(_) => "fs/read_text_file",
113            AgentRequest::RequestPermissionRequest(_) => "session/request_permission",
114            AgentRequest::CreateTerminalRequest(_) => "terminal/create",
115            AgentRequest::TerminalOutputRequest(_) => "terminal/output",
116            AgentRequest::ReleaseTerminalRequest(_) => "terminal/release",
117            AgentRequest::WaitForTerminalExitRequest(_) => "terminal/wait_for_exit",
118            AgentRequest::KillTerminalCommandRequest(_) => "terminal/kill",
119            AgentRequest::ExtMethodRequest(ext) => &ext.method,
120        }
121    }
122
123    fn to_untyped_message(&self) -> Result<crate::UntypedMessage, crate::Error> {
124        crate::UntypedMessage::new(self.method(), self)
125    }
126
127    fn parse_message(method: &str, params: &impl Serialize) -> Option<Result<Self, crate::Error>> {
128        let result = match method {
129            "fs/write_text_file" => json_cast(params).map(AgentRequest::WriteTextFileRequest),
130            "fs/read_text_file" => json_cast(params).map(AgentRequest::ReadTextFileRequest),
131            "session/request_permission" => {
132                json_cast(params).map(AgentRequest::RequestPermissionRequest)
133            }
134            "terminal/create" => json_cast(params).map(AgentRequest::CreateTerminalRequest),
135            "terminal/output" => json_cast(params).map(AgentRequest::TerminalOutputRequest),
136            "terminal/release" => json_cast(params).map(AgentRequest::ReleaseTerminalRequest),
137            "terminal/wait_for_exit" => {
138                json_cast(params).map(AgentRequest::WaitForTerminalExitRequest)
139            }
140            "terminal/kill" => json_cast(params).map(AgentRequest::KillTerminalCommandRequest),
141            _ => {
142                // Check for extension methods (prefixed with underscore)
143                if let Some(custom_method) = method.strip_prefix('_') {
144                    json_cast(params).map(|ext_req: crate::schema::ExtRequest| {
145                        AgentRequest::ExtMethodRequest(crate::schema::ExtRequest {
146                            method: custom_method.to_string().into(),
147                            params: ext_req.params,
148                        })
149                    })
150                } else {
151                    return None;
152                }
153            }
154        };
155
156        Some(result)
157    }
158}
159
160impl JrRequest for AgentRequest {
161    type Response = serde_json::Value;
162}
163
164impl JrMessage for AgentNotification {
165    fn method(&self) -> &str {
166        match self {
167            AgentNotification::SessionNotification(_) => "session/update",
168            AgentNotification::ExtNotification(ext) => &ext.method,
169        }
170    }
171
172    fn to_untyped_message(&self) -> Result<crate::UntypedMessage, crate::Error> {
173        crate::UntypedMessage::new(self.method(), self)
174    }
175
176    fn parse_message(method: &str, params: &impl Serialize) -> Option<Result<Self, crate::Error>> {
177        let result = match method {
178            "session/update" => json_cast(params).map(AgentNotification::SessionNotification),
179            _ => {
180                // Check for extension notifications (prefixed with underscore)
181                if let Some(custom_method) = method.strip_prefix('_') {
182                    json_cast(params).map(|ext_notif: crate::schema::ExtNotification| {
183                        AgentNotification::ExtNotification(crate::schema::ExtNotification {
184                            method: custom_method.to_string().into(),
185                            params: ext_notif.params,
186                        })
187                    })
188                } else {
189                    return None;
190                }
191            }
192        };
193
194        Some(result)
195    }
196}
197
198impl JrNotification for AgentNotification {}