1pub mod frame;
16
17use serde::{Deserialize, Serialize};
18use serde_json::Value;
19
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(untagged)]
24pub enum Id {
25 Num(i64),
26 Str(String),
27}
28
29impl From<i64> for Id {
30 fn from(n: i64) -> Self {
31 Id::Num(n)
32 }
33}
34impl From<String> for Id {
35 fn from(s: String) -> Self {
36 Id::Str(s)
37 }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct Request {
43 pub jsonrpc: Version,
44 pub id: Id,
45 pub method: String,
46 #[serde(skip_serializing_if = "Option::is_none", default)]
47 pub params: Option<Value>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct Notification {
53 pub jsonrpc: Version,
54 pub method: String,
55 #[serde(skip_serializing_if = "Option::is_none", default)]
56 pub params: Option<Value>,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct Response {
62 pub jsonrpc: Version,
63 pub id: Id,
64 #[serde(skip_serializing_if = "Option::is_none", default)]
65 pub result: Option<Value>,
66 #[serde(skip_serializing_if = "Option::is_none", default)]
67 pub error: Option<RpcError>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct RpcError {
76 pub code: i64,
77 pub message: String,
78 #[serde(skip_serializing_if = "Option::is_none", default)]
79 pub data: Option<Value>,
80}
81
82#[derive(Debug, Clone, Deserialize)]
87#[serde(untagged)]
88pub enum Incoming {
89 Request(Request),
95 Response(Response),
96 Notification(Notification),
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub struct Version;
103
104impl Serialize for Version {
105 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
106 s.serialize_str("2.0")
107 }
108}
109impl<'de> Deserialize<'de> for Version {
110 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
111 let s = String::deserialize(d)?;
112 if s == "2.0" {
113 Ok(Version)
114 } else {
115 Err(serde::de::Error::custom("jsonrpc version must be \"2.0\""))
116 }
117 }
118}
119
120impl Request {
121 pub fn new(id: impl Into<Id>, method: impl Into<String>, params: Option<Value>) -> Self {
122 Request {
123 jsonrpc: Version,
124 id: id.into(),
125 method: method.into(),
126 params,
127 }
128 }
129}
130
131impl Notification {
132 pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
133 Notification {
134 jsonrpc: Version,
135 method: method.into(),
136 params,
137 }
138 }
139}
140
141impl Response {
142 pub fn ok(id: Id, result: Value) -> Self {
143 Response {
144 jsonrpc: Version,
145 id,
146 result: Some(result),
147 error: None,
148 }
149 }
150 pub fn err(id: Id, code: i64, message: impl Into<String>) -> Self {
151 Response {
152 jsonrpc: Version,
153 id,
154 result: None,
155 error: Some(RpcError {
156 code,
157 message: message.into(),
158 data: None,
159 }),
160 }
161 }
162}
163
164pub const PARSE_ERROR: i64 = -32700;
166pub const INVALID_REQUEST: i64 = -32600;
167pub const METHOD_NOT_FOUND: i64 = -32601;
168pub const INVALID_PARAMS: i64 = -32602;
169pub const INTERNAL_ERROR: i64 = -32603;
170pub const RESOURCE_NOT_FOUND: i64 = -32002;
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn request_roundtrips() {
179 let r = Request::new(1, "tools/call", Some(serde_json::json!({"name": "x"})));
180 let s = serde_json::to_string(&r).unwrap();
181 assert!(s.contains("\"jsonrpc\":\"2.0\""));
182 assert!(s.contains("\"id\":1"));
183 let back: Request = serde_json::from_str(&s).unwrap();
184 assert_eq!(back.method, "tools/call");
185 }
186
187 #[test]
188 fn incoming_discriminates_response_vs_notification() {
189 let resp = r#"{"jsonrpc":"2.0","id":7,"result":{"ok":true}}"#;
190 match serde_json::from_str::<Incoming>(resp).unwrap() {
191 Incoming::Response(r) => assert_eq!(r.id, Id::Num(7)),
192 other => panic!("expected response, got {other:?}"),
193 }
194 let note = r#"{"jsonrpc":"2.0","method":"notifications/resources/updated","params":{"uri":"file://a"}}"#;
195 match serde_json::from_str::<Incoming>(note).unwrap() {
196 Incoming::Notification(n) => assert_eq!(n.method, "notifications/resources/updated"),
197 other => panic!("expected notification, got {other:?}"),
198 }
199 }
200
201 #[test]
202 fn incoming_parses_request_not_response() {
203 let req = r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}"#;
206 match serde_json::from_str::<Incoming>(req).unwrap() {
207 Incoming::Request(r) => assert_eq!(r.method, "initialize"),
208 other => panic!("expected request, got {other:?}"),
209 }
210 }
211
212 #[test]
213 fn bad_version_is_a_parse_error() {
214 let bad = r#"{"jsonrpc":"1.0","id":1,"method":"x"}"#;
215 assert!(serde_json::from_str::<Request>(bad).is_err());
216 }
217
218 #[test]
219 fn string_id_supported() {
220 let resp = r#"{"jsonrpc":"2.0","id":"abc","result":1}"#;
221 let r: Response = serde_json::from_str(resp).unwrap();
222 assert_eq!(r.id, Id::Str("abc".into()));
223 }
224}