1use serde::{Deserialize, Serialize};
2
3pub const PARSE_ERROR: i32 = -32700;
5pub const INVALID_REQUEST: i32 = -32600;
6pub const METHOD_NOT_FOUND: i32 = -32601;
7pub const INVALID_PARAMS: i32 = -32602;
8pub const INTERNAL_ERROR: i32 = -32603;
9
10pub const TASK_NOT_FOUND: i32 = -32001;
12pub const TASK_NOT_CANCELABLE: i32 = -32002;
13pub const PUSH_NOTIFICATION_NOT_SUPPORTED: i32 = -32003;
14pub const UNSUPPORTED_OPERATION: i32 = -32004;
15pub const CONTENT_TYPE_NOT_SUPPORTED: i32 = -32005;
16pub const INVALID_AGENT_RESPONSE: i32 = -32006;
17
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
20#[serde(untagged)]
21pub enum JsonRpcId {
22 Number(i64),
23 String(String),
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct JsonRpcRequest {
29 pub jsonrpc: String,
30 pub method: String,
31 #[serde(default)]
32 pub params: serde_json::Value,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub id: Option<JsonRpcId>,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct JsonRpcResponse {
40 pub jsonrpc: String,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub result: Option<serde_json::Value>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub error: Option<JsonRpcError>,
45 pub id: Option<JsonRpcId>,
46}
47
48impl JsonRpcResponse {
49 pub fn success(id: Option<JsonRpcId>, result: serde_json::Value) -> Self {
51 Self {
52 jsonrpc: "2.0".to_string(),
53 result: Some(result),
54 error: None,
55 id,
56 }
57 }
58
59 pub fn error(id: Option<JsonRpcId>, error: JsonRpcError) -> Self {
61 Self {
62 jsonrpc: "2.0".to_string(),
63 result: None,
64 error: Some(error),
65 id,
66 }
67 }
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct JsonRpcError {
73 pub code: i32,
74 pub message: String,
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub data: Option<serde_json::Value>,
77}
78
79impl JsonRpcError {
80 pub fn from_code(code: i32) -> Self {
82 let message = match code {
83 PARSE_ERROR => "Parse error",
84 INVALID_REQUEST => "Invalid Request",
85 METHOD_NOT_FOUND => "Method not found",
86 INVALID_PARAMS => "Invalid params",
87 INTERNAL_ERROR => "Internal error",
88 TASK_NOT_FOUND => "Task not found",
89 TASK_NOT_CANCELABLE => "Task not cancelable",
90 PUSH_NOTIFICATION_NOT_SUPPORTED => "Push notification not supported",
91 UNSUPPORTED_OPERATION => "Unsupported operation",
92 CONTENT_TYPE_NOT_SUPPORTED => "Incompatible content types",
93 INVALID_AGENT_RESPONSE => "Invalid agent response",
94 _ => "Unknown error",
95 };
96 Self {
97 code,
98 message: message.to_string(),
99 data: None,
100 }
101 }
102
103 pub fn with_message(code: i32, message: impl Into<String>) -> Self {
105 Self {
106 code,
107 message: message.into(),
108 data: None,
109 }
110 }
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
117pub enum A2aMethod {
118 MessageSend,
119 MessageStream,
120 TasksGet,
121 TasksCancel,
122 TasksResubscribe,
123 PushNotificationConfigSet,
124 PushNotificationConfigGet,
125 PushNotificationConfigList,
126 PushNotificationConfigDelete,
127}
128
129impl A2aMethod {
130 pub fn parse(method: &str) -> Option<Self> {
132 match method {
133 "message/send" => Some(Self::MessageSend),
134 "message/stream" => Some(Self::MessageStream),
135 "tasks/get" => Some(Self::TasksGet),
136 "tasks/cancel" => Some(Self::TasksCancel),
137 "tasks/resubscribe" => Some(Self::TasksResubscribe),
138 "tasks/pushNotificationConfig/set" => Some(Self::PushNotificationConfigSet),
139 "tasks/pushNotificationConfig/get" => Some(Self::PushNotificationConfigGet),
140 "tasks/pushNotificationConfig/list" => Some(Self::PushNotificationConfigList),
141 "tasks/pushNotificationConfig/delete" => Some(Self::PushNotificationConfigDelete),
142 _ => None,
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn parse_jsonrpc_request_from_json() {
153 let json = r#"{
154 "jsonrpc": "2.0",
155 "method": "message/send",
156 "params": {"message": {"messageId": "m1", "role": "user", "parts": []}},
157 "id": 42
158 }"#;
159 let req: JsonRpcRequest = serde_json::from_str(json).unwrap();
160 assert_eq!(req.jsonrpc, "2.0");
161 assert_eq!(req.method, "message/send");
162 assert_eq!(req.id, Some(JsonRpcId::Number(42)));
163 assert!(req.params.is_object());
164 }
165
166 #[test]
167 fn parse_jsonrpc_request_string_id() {
168 let json = r#"{"jsonrpc":"2.0","method":"tasks/get","params":{},"id":"abc-123"}"#;
169 let req: JsonRpcRequest = serde_json::from_str(json).unwrap();
170 assert_eq!(req.id, Some(JsonRpcId::String("abc-123".to_string())));
171 }
172
173 #[test]
174 fn parse_jsonrpc_request_null_id() {
175 let json = r#"{"jsonrpc":"2.0","method":"tasks/get","params":{},"id":null}"#;
177 let req: JsonRpcRequest = serde_json::from_str(json).unwrap();
178 assert_eq!(req.id, None);
179 }
180
181 #[test]
182 fn parse_jsonrpc_request_no_id() {
183 let json = r#"{"jsonrpc":"2.0","method":"tasks/get","params":{}}"#;
185 let req: JsonRpcRequest = serde_json::from_str(json).unwrap();
186 assert_eq!(req.id, None);
187 }
188
189 #[test]
190 fn parse_jsonrpc_request_default_params() {
191 let json = r#"{"jsonrpc":"2.0","method":"tasks/get"}"#;
192 let req: JsonRpcRequest = serde_json::from_str(json).unwrap();
193 assert!(req.params.is_null());
194 }
195
196 #[test]
197 fn jsonrpc_response_success() {
198 let id = Some(JsonRpcId::Number(1));
199 let resp = JsonRpcResponse::success(id, serde_json::json!({"status": "ok"}));
200 assert_eq!(resp.jsonrpc, "2.0");
201 assert!(resp.result.is_some());
202 assert!(resp.error.is_none());
203 assert_eq!(resp.result.unwrap()["status"], "ok");
204 }
205
206 #[test]
207 fn jsonrpc_response_error() {
208 let id = Some(JsonRpcId::String("req-1".to_string()));
209 let err = JsonRpcError::from_code(TASK_NOT_FOUND);
210 let resp = JsonRpcResponse::error(id, err);
211 assert_eq!(resp.jsonrpc, "2.0");
212 assert!(resp.result.is_none());
213 assert!(resp.error.is_some());
214 let e = resp.error.unwrap();
215 assert_eq!(e.code, TASK_NOT_FOUND);
216 assert_eq!(e.message, "Task not found");
217 }
218
219 #[test]
220 fn jsonrpc_response_round_trip() {
221 let resp =
222 JsonRpcResponse::success(Some(JsonRpcId::Number(7)), serde_json::json!({"data": 42}));
223 let json = serde_json::to_string(&resp).unwrap();
224 let parsed: JsonRpcResponse = serde_json::from_str(&json).unwrap();
225 assert_eq!(parsed.jsonrpc, "2.0");
226 assert_eq!(parsed.result.unwrap()["data"], 42);
227 assert!(parsed.error.is_none());
228 }
229
230 #[test]
231 fn jsonrpc_error_from_code_all_standard() {
232 let cases = [
233 (PARSE_ERROR, "Parse error"),
234 (INVALID_REQUEST, "Invalid Request"),
235 (METHOD_NOT_FOUND, "Method not found"),
236 (INVALID_PARAMS, "Invalid params"),
237 (INTERNAL_ERROR, "Internal error"),
238 (TASK_NOT_FOUND, "Task not found"),
239 (TASK_NOT_CANCELABLE, "Task not cancelable"),
240 (
241 PUSH_NOTIFICATION_NOT_SUPPORTED,
242 "Push notification not supported",
243 ),
244 (UNSUPPORTED_OPERATION, "Unsupported operation"),
245 (CONTENT_TYPE_NOT_SUPPORTED, "Incompatible content types"),
246 (INVALID_AGENT_RESPONSE, "Invalid agent response"),
247 ];
248 for (code, expected_msg) in &cases {
249 let err = JsonRpcError::from_code(*code);
250 assert_eq!(err.code, *code);
251 assert_eq!(err.message, *expected_msg, "code {code}");
252 assert!(err.data.is_none());
253 }
254 }
255
256 #[test]
257 fn jsonrpc_error_from_code_unknown() {
258 let err = JsonRpcError::from_code(-99999);
259 assert_eq!(err.code, -99999);
260 assert_eq!(err.message, "Unknown error");
261 }
262
263 #[test]
264 fn jsonrpc_error_with_message() {
265 let err = JsonRpcError::with_message(INTERNAL_ERROR, "something broke");
266 assert_eq!(err.code, INTERNAL_ERROR);
267 assert_eq!(err.message, "something broke");
268 }
269
270 #[test]
271 fn a2a_method_parse_all() {
272 let cases = [
273 ("message/send", A2aMethod::MessageSend),
274 ("message/stream", A2aMethod::MessageStream),
275 ("tasks/get", A2aMethod::TasksGet),
276 ("tasks/cancel", A2aMethod::TasksCancel),
277 ("tasks/resubscribe", A2aMethod::TasksResubscribe),
278 (
279 "tasks/pushNotificationConfig/set",
280 A2aMethod::PushNotificationConfigSet,
281 ),
282 (
283 "tasks/pushNotificationConfig/get",
284 A2aMethod::PushNotificationConfigGet,
285 ),
286 (
287 "tasks/pushNotificationConfig/list",
288 A2aMethod::PushNotificationConfigList,
289 ),
290 (
291 "tasks/pushNotificationConfig/delete",
292 A2aMethod::PushNotificationConfigDelete,
293 ),
294 ];
295 for (method_str, expected) in &cases {
296 let parsed = A2aMethod::parse(method_str);
297 assert_eq!(parsed, Some(expected.clone()), "parse({method_str})");
298 }
299 }
300
301 #[test]
302 fn a2a_method_parse_unknown() {
303 assert_eq!(A2aMethod::parse("unknown/method"), None);
304 assert_eq!(A2aMethod::parse(""), None);
305 assert_eq!(A2aMethod::parse("message/send/extra"), None);
306 }
307}