1use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use serde_json::Value;
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct JsonRpcRequest {
14 pub jsonrpc: String,
15 pub id: JsonRpcId,
16 pub method: String,
17 #[serde(default, skip_serializing_if = "Option::is_none")]
18 pub params: Option<Value>,
19}
20
21impl JsonRpcRequest {
22 pub fn new(id: JsonRpcId, method: impl Into<String>, params: Option<Value>) -> Self {
23 JsonRpcRequest {
24 jsonrpc: "2.0".to_string(),
25 id,
26 method: method.into(),
27 params,
28 }
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34pub struct JsonRpcResponse {
35 pub jsonrpc: String,
36 pub id: JsonRpcId,
37 #[serde(default, skip_serializing_if = "Option::is_none")]
38 pub result: Option<Value>,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub error: Option<JsonRpcError>,
41}
42
43impl JsonRpcResponse {
44 pub fn success(id: JsonRpcId, result: Value) -> Self {
45 JsonRpcResponse {
46 jsonrpc: "2.0".to_string(),
47 id,
48 result: Some(result),
49 error: None,
50 }
51 }
52
53 pub fn error(id: JsonRpcId, error: JsonRpcError) -> Self {
54 JsonRpcResponse {
55 jsonrpc: "2.0".to_string(),
56 id,
57 result: None,
58 error: Some(error),
59 }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
65pub struct JsonRpcError {
66 pub code: i32,
67 pub message: String,
68 #[serde(default, skip_serializing_if = "Option::is_none")]
69 pub data: Option<Value>,
70}
71
72#[derive(Debug, Clone, PartialEq)]
78pub enum JsonRpcId {
79 String(String),
80 Number(i64),
81 Null,
82}
83
84impl Serialize for JsonRpcId {
85 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
86 match self {
87 JsonRpcId::String(s) => serializer.serialize_str(s),
88 JsonRpcId::Number(n) => serializer.serialize_i64(*n),
89 JsonRpcId::Null => serializer.serialize_none(),
90 }
91 }
92}
93
94impl<'de> Deserialize<'de> for JsonRpcId {
95 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
96 let v = Value::deserialize(deserializer)?;
97 match v {
98 Value::String(s) => Ok(JsonRpcId::String(s)),
99 Value::Number(n) => {
100 if let Some(i) = n.as_i64() {
101 Ok(JsonRpcId::Number(i))
102 } else {
103 Err(serde::de::Error::custom(
104 "JSON-RPC id must be a non-fractional number",
105 ))
106 }
107 }
108 Value::Null => Ok(JsonRpcId::Null),
109 _ => Err(serde::de::Error::custom(
110 "JSON-RPC id must be string, integer, or null",
111 )),
112 }
113 }
114}
115
116impl From<String> for JsonRpcId {
117 fn from(s: String) -> Self {
118 JsonRpcId::String(s)
119 }
120}
121
122impl From<&str> for JsonRpcId {
123 fn from(s: &str) -> Self {
124 JsonRpcId::String(s.to_string())
125 }
126}
127
128impl From<i64> for JsonRpcId {
129 fn from(n: i64) -> Self {
130 JsonRpcId::Number(n)
131 }
132}
133
134pub mod methods {
139 pub const SEND_MESSAGE: &str = "SendMessage";
140 pub const SEND_STREAMING_MESSAGE: &str = "SendStreamingMessage";
141 pub const GET_TASK: &str = "GetTask";
142 pub const LIST_TASKS: &str = "ListTasks";
143 pub const CANCEL_TASK: &str = "CancelTask";
144 pub const SUBSCRIBE_TO_TASK: &str = "SubscribeToTask";
145 pub const CREATE_PUSH_CONFIG: &str = "CreateTaskPushNotificationConfig";
146 pub const GET_PUSH_CONFIG: &str = "GetTaskPushNotificationConfig";
147 pub const LIST_PUSH_CONFIGS: &str = "ListTaskPushNotificationConfigs";
148 pub const DELETE_PUSH_CONFIG: &str = "DeleteTaskPushNotificationConfig";
149 pub const GET_EXTENDED_AGENT_CARD: &str = "GetExtendedAgentCard";
150
151 pub fn is_streaming(method: &str) -> bool {
152 matches!(method, SEND_STREAMING_MESSAGE | SUBSCRIBE_TO_TASK)
153 }
154
155 pub fn is_valid(method: &str) -> bool {
156 matches!(
157 method,
158 SEND_MESSAGE
159 | SEND_STREAMING_MESSAGE
160 | GET_TASK
161 | LIST_TASKS
162 | CANCEL_TASK
163 | SUBSCRIBE_TO_TASK
164 | CREATE_PUSH_CONFIG
165 | GET_PUSH_CONFIG
166 | LIST_PUSH_CONFIGS
167 | DELETE_PUSH_CONFIG
168 | GET_EXTENDED_AGENT_CARD
169 )
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn test_jsonrpc_id_string() {
179 let id = JsonRpcId::String("abc".into());
180 let json = serde_json::to_string(&id).unwrap();
181 assert_eq!(json, r#""abc""#);
182 let back: JsonRpcId = serde_json::from_str(&json).unwrap();
183 assert_eq!(back, JsonRpcId::String("abc".into()));
184 }
185
186 #[test]
187 fn test_jsonrpc_id_number() {
188 let id = JsonRpcId::Number(42);
189 let json = serde_json::to_string(&id).unwrap();
190 assert_eq!(json, "42");
191 let back: JsonRpcId = serde_json::from_str(&json).unwrap();
192 assert_eq!(back, JsonRpcId::Number(42));
193 }
194
195 #[test]
196 fn test_jsonrpc_request_roundtrip() {
197 let req = JsonRpcRequest::new(
198 JsonRpcId::String("req-1".into()),
199 methods::SEND_MESSAGE,
200 Some(serde_json::json!({"key": "value"})),
201 );
202 let json = serde_json::to_string(&req).unwrap();
203 let back: JsonRpcRequest = serde_json::from_str(&json).unwrap();
204 assert_eq!(back.method, methods::SEND_MESSAGE);
205 assert_eq!(back.jsonrpc, "2.0");
206 }
207
208 #[test]
209 fn test_jsonrpc_id_null() {
210 let id = JsonRpcId::Null;
211 let json = serde_json::to_string(&id).unwrap();
212 assert_eq!(json, "null");
213 let back: JsonRpcId = serde_json::from_str(&json).unwrap();
214 assert_eq!(back, JsonRpcId::Null);
215 }
216
217 #[test]
218 fn test_jsonrpc_id_invalid_type() {
219 let result = serde_json::from_str::<JsonRpcId>(r#"[1,2]"#);
220 assert!(result.is_err());
221 }
222
223 #[test]
224 fn test_jsonrpc_id_fractional_number() {
225 let result = serde_json::from_str::<JsonRpcId>("3.14");
226 assert!(result.is_err());
227 }
228
229 #[test]
230 fn test_jsonrpc_id_from_string() {
231 let id: JsonRpcId = String::from("test").into();
232 assert_eq!(id, JsonRpcId::String("test".into()));
233 }
234
235 #[test]
236 fn test_jsonrpc_id_from_str() {
237 let id: JsonRpcId = "test".into();
238 assert_eq!(id, JsonRpcId::String("test".into()));
239 }
240
241 #[test]
242 fn test_jsonrpc_id_from_i64() {
243 let id: JsonRpcId = 42i64.into();
244 assert_eq!(id, JsonRpcId::Number(42));
245 }
246
247 #[test]
248 fn test_jsonrpc_response_success() {
249 let resp =
250 JsonRpcResponse::success(JsonRpcId::Number(1), serde_json::json!({"status": "ok"}));
251 assert_eq!(resp.jsonrpc, "2.0");
252 assert!(resp.result.is_some());
253 assert!(resp.error.is_none());
254 let json = serde_json::to_string(&resp).unwrap();
255 let back: JsonRpcResponse = serde_json::from_str(&json).unwrap();
256 assert_eq!(back.id, JsonRpcId::Number(1));
257 }
258
259 #[test]
260 fn test_jsonrpc_response_error() {
261 let err = JsonRpcError {
262 code: -32600,
263 message: "invalid".into(),
264 data: None,
265 };
266 let resp = JsonRpcResponse::error(JsonRpcId::String("e1".into()), err);
267 assert!(resp.result.is_none());
268 assert!(resp.error.is_some());
269 assert_eq!(resp.error.as_ref().unwrap().code, -32600);
270 }
271
272 #[test]
273 fn test_jsonrpc_error_with_data() {
274 let err = JsonRpcError {
275 code: -32000,
276 message: "custom".into(),
277 data: Some(serde_json::json!({"detail": "info"})),
278 };
279 let json = serde_json::to_string(&err).unwrap();
280 let back: JsonRpcError = serde_json::from_str(&json).unwrap();
281 assert!(back.data.is_some());
282 }
283
284 #[test]
285 fn test_jsonrpc_request_no_params() {
286 let req = JsonRpcRequest::new(JsonRpcId::Number(1), methods::GET_TASK, None);
287 let json = serde_json::to_string(&req).unwrap();
288 assert!(!json.contains("params"));
289 }
290
291 #[test]
292 fn test_methods_is_streaming() {
293 assert!(methods::is_streaming(methods::SEND_STREAMING_MESSAGE));
294 assert!(methods::is_streaming(methods::SUBSCRIBE_TO_TASK));
295 assert!(!methods::is_streaming("message.stream"));
296 assert!(!methods::is_streaming("tasks.resubscribe"));
297 assert!(!methods::is_streaming(methods::SEND_MESSAGE));
298 assert!(!methods::is_streaming(methods::GET_TASK));
299 assert!(!methods::is_streaming("unknown"));
300 }
301
302 #[test]
303 fn test_methods_is_valid() {
304 assert!(methods::is_valid(methods::SEND_MESSAGE));
305 assert!(methods::is_valid(methods::SEND_STREAMING_MESSAGE));
306 assert!(methods::is_valid(methods::GET_TASK));
307 assert!(methods::is_valid(methods::LIST_TASKS));
308 assert!(methods::is_valid(methods::CANCEL_TASK));
309 assert!(methods::is_valid(methods::SUBSCRIBE_TO_TASK));
310 assert!(methods::is_valid(methods::CREATE_PUSH_CONFIG));
311 assert!(methods::is_valid(methods::GET_PUSH_CONFIG));
312 assert!(methods::is_valid(methods::LIST_PUSH_CONFIGS));
313 assert!(methods::is_valid(methods::DELETE_PUSH_CONFIG));
314 assert!(methods::is_valid(methods::GET_EXTENDED_AGENT_CARD));
315 assert!(!methods::is_valid("message.send"));
316 assert!(!methods::is_valid("message.stream"));
317 assert!(!methods::is_valid("tasks.get"));
318 assert!(!methods::is_valid("tasks.list"));
319 assert!(!methods::is_valid("tasks.cancel"));
320 assert!(!methods::is_valid("tasks.resubscribe"));
321 assert!(!methods::is_valid("push-config.set"));
322 assert!(!methods::is_valid("push-config.get"));
323 assert!(!methods::is_valid("push-config.list"));
324 assert!(!methods::is_valid("push-config.delete"));
325 assert!(!methods::is_valid("agent-card.extended.get"));
326 assert!(!methods::is_valid("unknown.method"));
327 }
328}