1use serde::{Deserialize, Deserializer, de::DeserializeOwned};
16use serde_json::value::RawValue;
17use serde_json::{Map, Value};
18
19#[derive(Debug, Clone)]
21pub struct JsonRpcEnvelope {
22 pub id: Option<JsonRpcId>,
23 pub method: Option<String>,
24 pub params: Option<Box<RawValue>>,
25 pub result: Option<Box<RawValue>>,
26 pub error: Option<JsonRpcError>,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Hash)]
33pub enum JsonRpcId {
34 Number(i64),
35 String(String),
36 Null,
37}
38
39#[derive(Debug, Clone)]
42pub struct JsonRpcError {
43 pub code: i32,
44 pub message: String,
45 pub data: Option<Box<RawValue>>,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
52pub enum ParseError {
53 NotJson,
55 NotJsonRpc20,
57 InvalidShape,
61}
62
63#[derive(Deserialize)]
64struct Raw {
65 #[serde(default)]
66 jsonrpc: Option<String>,
67 #[serde(default, deserialize_with = "some_value")]
72 id: Option<serde_json::Value>,
73 #[serde(default)]
74 method: Option<String>,
75 #[serde(default)]
76 params: Option<Box<RawValue>>,
77 #[serde(default)]
78 result: Option<Box<RawValue>>,
79 #[serde(default)]
80 error: Option<RawError>,
81}
82
83fn some_value<'de, D>(d: D) -> Result<Option<serde_json::Value>, D::Error>
84where
85 D: Deserializer<'de>,
86{
87 serde_json::Value::deserialize(d).map(Some)
88}
89
90#[derive(Deserialize)]
91struct RawError {
92 code: i32,
93 message: String,
94 #[serde(default)]
95 data: Option<Box<RawValue>>,
96}
97
98impl JsonRpcEnvelope {
99 pub fn parse(bytes: &[u8]) -> Result<Self, ParseError> {
101 if first_non_ws(bytes) == Some(b'[') {
102 return Err(ParseError::InvalidShape);
103 }
104
105 let raw: Raw = serde_json::from_slice(bytes).map_err(|_| ParseError::NotJson)?;
106
107 if raw.jsonrpc.as_deref() != Some("2.0") {
108 return Err(ParseError::NotJsonRpc20);
109 }
110
111 let id = match raw.id {
112 None => None,
113 Some(serde_json::Value::Null) => Some(JsonRpcId::Null),
114 Some(serde_json::Value::Number(n)) => Some(JsonRpcId::Number(
115 n.as_i64().ok_or(ParseError::InvalidShape)?,
116 )),
117 Some(serde_json::Value::String(s)) => Some(JsonRpcId::String(s)),
118 Some(_) => return Err(ParseError::InvalidShape),
119 };
120
121 let error = raw.error.map(|e| JsonRpcError {
122 code: e.code,
123 message: e.message,
124 data: e.data,
125 });
126
127 let shape = (
128 raw.method.is_some(),
129 id.is_some(),
130 raw.result.is_some(),
131 error.is_some(),
132 );
133 let valid = matches!(
135 shape,
136 (true, true, false, false) | (true, false, false, false) | (false, true, true, false) | (false, true, false, true) );
141 if !valid {
142 return Err(ParseError::InvalidShape);
143 }
144
145 Ok(JsonRpcEnvelope {
146 id,
147 method: raw.method,
148 params: raw.params,
149 result: raw.result,
150 error,
151 })
152 }
153
154 pub fn params_as<T: DeserializeOwned>(&self) -> Option<T> {
157 let raw = self.params.as_ref()?;
158 serde_json::from_str(raw.get()).ok()
159 }
160
161 pub fn result_as<T: DeserializeOwned>(&self) -> Option<T> {
164 let raw = self.result.as_ref()?;
165 serde_json::from_str(raw.get()).ok()
166 }
167
168 pub fn to_bytes(&self) -> Vec<u8> {
176 let mut map = Map::with_capacity(5);
177 map.insert("jsonrpc".into(), Value::String("2.0".into()));
178 if let Some(id) = &self.id {
179 map.insert("id".into(), id_to_value(id));
180 }
181 if let Some(method) = &self.method {
182 map.insert("method".into(), Value::String(method.clone()));
183 }
184 if let Some(params) = &self.params {
185 map.insert(
186 "params".into(),
187 serde_json::from_str(params.get()).unwrap_or(Value::Null),
188 );
189 }
190 if let Some(result) = &self.result {
191 map.insert(
192 "result".into(),
193 serde_json::from_str(result.get()).unwrap_or(Value::Null),
194 );
195 }
196 if let Some(error) = &self.error {
197 let mut err = Map::with_capacity(3);
198 err.insert("code".into(), Value::Number((error.code as i64).into()));
199 err.insert("message".into(), Value::String(error.message.clone()));
200 if let Some(data) = &error.data {
201 err.insert(
202 "data".into(),
203 serde_json::from_str(data.get()).unwrap_or(Value::Null),
204 );
205 }
206 map.insert("error".into(), Value::Object(err));
207 }
208 serde_json::to_vec(&Value::Object(map)).unwrap_or_default()
209 }
210}
211
212fn id_to_value(id: &JsonRpcId) -> Value {
213 match id {
214 JsonRpcId::Number(n) => Value::Number((*n).into()),
215 JsonRpcId::String(s) => Value::String(s.clone()),
216 JsonRpcId::Null => Value::Null,
217 }
218}
219
220fn first_non_ws(bytes: &[u8]) -> Option<u8> {
221 bytes.iter().copied().find(|b| !b.is_ascii_whitespace())
222}
223
224#[cfg(test)]
225#[allow(non_snake_case)]
226mod tests {
227 use super::*;
228 use serde::Deserialize;
229
230 #[derive(Debug, Deserialize, PartialEq)]
231 struct Greet {
232 name: String,
233 }
234
235 #[test]
238 fn parse__request_shape() {
239 let env = JsonRpcEnvelope::parse(
240 br#"{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"x":1}}"#,
241 )
242 .unwrap();
243 assert_eq!(env.id, Some(JsonRpcId::Number(1)));
244 assert_eq!(env.method.as_deref(), Some("tools/list"));
245 assert!(env.params.is_some());
246 assert!(env.result.is_none());
247 assert!(env.error.is_none());
248 }
249
250 #[test]
251 fn parse__notification_shape() {
252 let env = JsonRpcEnvelope::parse(
253 br#"{"jsonrpc":"2.0","method":"notifications/progress","params":{"p":0.5}}"#,
254 )
255 .unwrap();
256 assert!(env.id.is_none());
257 assert_eq!(env.method.as_deref(), Some("notifications/progress"));
258 }
259
260 #[test]
261 fn parse__result_shape() {
262 let env =
263 JsonRpcEnvelope::parse(br#"{"jsonrpc":"2.0","id":"r1","result":{"ok":true}}"#).unwrap();
264 assert_eq!(env.id, Some(JsonRpcId::String("r1".into())));
265 assert!(env.result.is_some());
266 }
267
268 #[test]
269 fn parse__error_shape() {
270 let env = JsonRpcEnvelope::parse(
271 br#"{"jsonrpc":"2.0","id":7,"error":{"code":-32600,"message":"invalid"}}"#,
272 )
273 .unwrap();
274 let err = env.error.unwrap();
275 assert_eq!(err.code, -32600);
276 assert_eq!(err.message, "invalid");
277 }
278
279 #[test]
280 fn parse__null_id_accepted() {
281 let env = JsonRpcEnvelope::parse(
282 br#"{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"parse error"}}"#,
283 )
284 .unwrap();
285 assert_eq!(env.id, Some(JsonRpcId::Null));
286 }
287
288 #[test]
289 fn parse__id_fractional_number_rejected() {
290 let err =
291 JsonRpcEnvelope::parse(br#"{"jsonrpc":"2.0","id":1.5,"method":"x"}"#).unwrap_err();
292 assert_eq!(err, ParseError::InvalidShape);
293 }
294
295 #[test]
298 fn parse__empty_body_returns_not_json() {
299 assert_eq!(
300 JsonRpcEnvelope::parse(b"").unwrap_err(),
301 ParseError::NotJson
302 );
303 }
304
305 #[test]
306 fn parse__garbage_bytes_return_not_json() {
307 assert_eq!(
308 JsonRpcEnvelope::parse(b"not json at all").unwrap_err(),
309 ParseError::NotJson,
310 );
311 }
312
313 #[test]
314 fn parse__missing_jsonrpc_field() {
315 let err = JsonRpcEnvelope::parse(br#"{"id":1,"method":"foo"}"#).unwrap_err();
316 assert_eq!(err, ParseError::NotJsonRpc20);
317 }
318
319 #[test]
320 fn parse__wrong_jsonrpc_version() {
321 let err =
322 JsonRpcEnvelope::parse(br#"{"jsonrpc":"1.0","id":1,"method":"foo"}"#).unwrap_err();
323 assert_eq!(err, ParseError::NotJsonRpc20);
324 }
325
326 #[test]
327 fn parse__bare_jsonrpc_is_invalid_shape() {
328 let err = JsonRpcEnvelope::parse(br#"{"jsonrpc":"2.0"}"#).unwrap_err();
329 assert_eq!(err, ParseError::InvalidShape);
330 }
331
332 #[test]
333 fn parse__top_level_array_rejected() {
334 let err = JsonRpcEnvelope::parse(br#"[{"jsonrpc":"2.0","method":"x"}]"#).unwrap_err();
335 assert_eq!(err, ParseError::InvalidShape);
336 }
337
338 #[test]
339 fn parse__top_level_array_with_leading_ws_rejected() {
340 let err = JsonRpcEnvelope::parse(b" [ ]").unwrap_err();
341 assert_eq!(err, ParseError::InvalidShape);
342 }
343
344 #[test]
345 fn parse__both_result_and_error_rejected() {
346 let err = JsonRpcEnvelope::parse(
347 br#"{"jsonrpc":"2.0","id":1,"result":{},"error":{"code":-1,"message":"x"}}"#,
348 )
349 .unwrap_err();
350 assert_eq!(err, ParseError::InvalidShape);
351 }
352
353 #[test]
354 fn parse__response_without_id_rejected() {
355 let err = JsonRpcEnvelope::parse(br#"{"jsonrpc":"2.0","result":{}}"#).unwrap_err();
356 assert_eq!(err, ParseError::InvalidShape);
357 }
358
359 #[test]
362 fn params_as__deserializes_on_match() {
363 let env = JsonRpcEnvelope::parse(
364 br#"{"jsonrpc":"2.0","id":1,"method":"greet","params":{"name":"rod"}}"#,
365 )
366 .unwrap();
367 assert_eq!(env.params_as::<Greet>(), Some(Greet { name: "rod".into() }));
368 }
369
370 #[test]
371 fn params_as__none_on_mismatch() {
372 let env = JsonRpcEnvelope::parse(
373 br#"{"jsonrpc":"2.0","id":1,"method":"greet","params":{"wrong":1}}"#,
374 )
375 .unwrap();
376 assert!(env.params_as::<Greet>().is_none());
377 }
378
379 #[test]
380 fn params_as__none_when_absent() {
381 let env = JsonRpcEnvelope::parse(br#"{"jsonrpc":"2.0","id":1,"method":"greet"}"#).unwrap();
382 assert!(env.params_as::<Greet>().is_none());
383 }
384
385 #[test]
386 fn result_as__deserializes_on_match() {
387 let env =
388 JsonRpcEnvelope::parse(br#"{"jsonrpc":"2.0","id":1,"result":{"name":"rod"}}"#).unwrap();
389 assert_eq!(env.result_as::<Greet>(), Some(Greet { name: "rod".into() }));
390 }
391}