1pub mod frame;
15
16use serde::{Deserialize, Serialize};
17use serde_json::Value;
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
22#[serde(untagged)]
23pub enum Id {
24 Num(i64),
25 Str(String),
26}
27
28impl From<i64> for Id {
29 fn from(n: i64) -> Self {
30 Id::Num(n)
31 }
32}
33impl From<String> for Id {
34 fn from(s: String) -> Self {
35 Id::Str(s)
36 }
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct Request {
42 pub jsonrpc: Version,
43 pub id: Id,
44 pub method: String,
45 #[serde(skip_serializing_if = "Option::is_none", default)]
46 pub params: Option<Value>,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct Notification {
52 pub jsonrpc: Version,
53 pub method: String,
54 #[serde(skip_serializing_if = "Option::is_none", default)]
55 pub params: Option<Value>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct Response {
61 pub jsonrpc: Version,
62 pub id: Id,
63 #[serde(skip_serializing_if = "Option::is_none", default)]
64 pub result: Option<Value>,
65 #[serde(skip_serializing_if = "Option::is_none", default)]
66 pub error: Option<RpcError>,
67}
68
69#[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}