1use std::io::{self, BufRead, Write};
4
5use serde::de::DeserializeOwned;
6use serde::{Deserialize, Serialize};
7
8use crate::{
9 ExtractError, LspOrDapResponse, LspResult, RequestId, ResponseError, invalid_data_fmt,
10 read_msg_text, write_msg_text,
11};
12
13#[derive(Serialize, Deserialize, Debug, Clone)]
15#[serde(untagged)]
16pub enum Message {
17 Request(Request),
19 Response(Response),
21 Notification(Notification),
23}
24
25impl From<Request> for Message {
26 fn from(request: Request) -> Message {
27 Message::Request(request)
28 }
29}
30
31impl From<Response> for Message {
32 fn from(response: Response) -> Message {
33 Message::Response(response)
34 }
35}
36
37impl From<Notification> for Message {
38 fn from(notification: Notification) -> Message {
39 Message::Notification(notification)
40 }
41}
42
43impl Message {
44 pub fn read(r: &mut impl BufRead) -> io::Result<Option<Message>> {
46 let text = match read_msg_text(r)? {
47 None => return Ok(None),
48 Some(text) => text,
49 };
50
51 let msg = match serde_json::from_str(&text) {
52 Ok(msg) => msg,
53 Err(e) => {
54 return Err(invalid_data_fmt!("malformed LSP payload: {e:?}"));
55 }
56 };
57
58 Ok(Some(msg))
59 }
60
61 pub fn write(self, w: &mut impl Write) -> io::Result<()> {
63 #[derive(Serialize)]
64 struct JsonRpc {
65 jsonrpc: &'static str,
66 #[serde(flatten)]
67 msg: Message,
68 }
69 let text = serde_json::to_string(&JsonRpc {
70 jsonrpc: "2.0",
71 msg: self,
72 })?;
73 write_msg_text(w, &text)
74 }
75}
76
77#[derive(Debug, Serialize, Deserialize, Clone)]
79pub struct Request {
80 pub id: RequestId,
83 pub method: String,
85 #[serde(default = "serde_json::Value::default")]
87 #[serde(skip_serializing_if = "serde_json::Value::is_null")]
88 pub params: serde_json::Value,
89}
90
91impl Request {
92 pub fn new<P: serde::Serialize>(id: RequestId, method: String, params: P) -> Request {
94 Request {
95 id,
96 method,
97 params: serde_json::to_value(params).unwrap(),
98 }
99 }
100
101 pub fn extract<P: DeserializeOwned>(
103 self,
104 method: &str,
105 ) -> Result<(RequestId, P), ExtractError<Request>> {
106 if self.method != method {
107 return Err(ExtractError::MethodMismatch(self));
108 }
109 match serde_json::from_value(self.params) {
110 Ok(params) => Ok((self.id, params)),
111 Err(error) => Err(ExtractError::JsonError {
112 method: self.method,
113 error,
114 }),
115 }
116 }
117}
118
119#[derive(Debug, Serialize, Deserialize, Clone)]
121pub struct Response {
122 pub id: RequestId,
126 #[serde(skip_serializing_if = "Option::is_none")]
129 pub result: Option<serde_json::Value>,
130 #[serde(skip_serializing_if = "Option::is_none")]
133 pub error: Option<ResponseError>,
134}
135
136impl Response {
137 pub fn new(id: RequestId, result: LspResult<serde_json::Value>) -> Response {
139 match result {
140 Ok(result) => Response {
141 id,
142 result: Some(result),
143 error: None,
144 },
145 Err(err) => Response {
146 id,
147 result: None,
148 error: Some(err),
149 },
150 }
151 }
152}
153
154#[derive(Debug, Serialize, Deserialize, Clone)]
156pub struct Notification {
157 pub method: String,
159 #[serde(default = "serde_json::Value::default")]
161 #[serde(skip_serializing_if = "serde_json::Value::is_null")]
162 pub params: serde_json::Value,
163}
164
165impl Notification {
166 pub fn new(method: String, params: impl serde::Serialize) -> Notification {
168 Notification {
169 method,
170 params: serde_json::to_value(params).unwrap(),
171 }
172 }
173
174 pub fn extract<P: DeserializeOwned>(
176 self,
177 method: &str,
178 ) -> Result<P, ExtractError<Notification>> {
179 if self.method != method {
180 return Err(ExtractError::MethodMismatch(self));
181 }
182 match serde_json::from_value(self.params) {
183 Ok(params) => Ok(params),
184 Err(error) => Err(ExtractError::JsonError {
185 method: self.method,
186 error,
187 }),
188 }
189 }
190
191 #[cfg(all(feature = "server", feature = "system"))]
192 pub(crate) fn is_exit(&self) -> bool {
193 self.method == "exit"
194 }
195}
196
197impl TryFrom<crate::Message> for Message {
198 type Error = anyhow::Error;
199
200 fn try_from(msg: crate::Message) -> anyhow::Result<Self> {
201 match msg {
202 crate::Message::Lsp(msg) => Ok(msg),
203 #[cfg(feature = "dap")]
204 crate::Message::Dap(msg) => anyhow::bail!("unexpected DAP message: {msg:?}"),
205 }
206 }
207}
208
209impl From<Request> for crate::Message {
210 fn from(request: Request) -> crate::Message {
211 crate::Message::Lsp(request.into())
212 }
213}
214
215impl From<Response> for crate::Message {
216 fn from(response: Response) -> crate::Message {
217 crate::Message::Lsp(response.into())
218 }
219}
220
221impl From<Notification> for crate::Message {
222 fn from(notification: Notification) -> crate::Message {
223 crate::Message::Lsp(notification.into())
224 }
225}
226
227impl From<Response> for LspOrDapResponse {
228 fn from(resp: Response) -> Self {
229 Self::Lsp(resp)
230 }
231}
232
233impl TryFrom<LspOrDapResponse> for Response {
234 type Error = anyhow::Error;
235
236 fn try_from(resp: LspOrDapResponse) -> anyhow::Result<Self> {
237 match resp {
238 LspOrDapResponse::Lsp(resp) => Ok(resp),
239 #[cfg(feature = "dap")]
240 LspOrDapResponse::Dap(_) => anyhow::bail!("unexpected DAP response"),
241 }
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::{Message, Notification, Request, RequestId};
248
249 #[test]
250 fn shutdown_with_explicit_null() {
251 let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\", \"params\": null }";
252 let msg: Message = serde_json::from_str(text).unwrap();
253
254 assert!(
255 matches!(msg, Message::Request(req) if req.id == 3.into() && req.method == "shutdown")
256 );
257 }
258
259 #[test]
260 fn shutdown_with_no_params() {
261 let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\"}";
262 let msg: Message = serde_json::from_str(text).unwrap();
263
264 assert!(
265 matches!(msg, Message::Request(req) if req.id == 3.into() && req.method == "shutdown")
266 );
267 }
268
269 #[test]
270 fn notification_with_explicit_null() {
271 let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\", \"params\": null }";
272 let msg: Message = serde_json::from_str(text).unwrap();
273
274 assert!(matches!(msg, Message::Notification(not) if not.method == "exit"));
275 }
276
277 #[test]
278 fn notification_with_no_params() {
279 let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\"}";
280 let msg: Message = serde_json::from_str(text).unwrap();
281
282 assert!(matches!(msg, Message::Notification(not) if not.method == "exit"));
283 }
284
285 #[test]
286 fn serialize_request_with_null_params() {
287 let msg = Message::Request(Request {
288 id: RequestId::from(3),
289 method: "shutdown".into(),
290 params: serde_json::Value::Null,
291 });
292 let serialized = serde_json::to_string(&msg).unwrap();
293
294 assert_eq!("{\"id\":3,\"method\":\"shutdown\"}", serialized);
295 }
296
297 #[test]
298 fn serialize_notification_with_null_params() {
299 let msg = Message::Notification(Notification {
300 method: "exit".into(),
301 params: serde_json::Value::Null,
302 });
303 let serialized = serde_json::to_string(&msg).unwrap();
304
305 assert_eq!("{\"method\":\"exit\"}", serialized);
306 }
307}