1use std::io::{self, BufRead, Write};
4
5use serde::de::DeserializeOwned;
6use serde::{Deserialize, Serialize};
7
8use crate::{
9 invalid_data_fmt, read_msg_text, write_msg_text, ExtractError, LspOrDapResponse, RequestId,
10 ResponseError,
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_ok<R: serde::Serialize>(id: RequestId, result: R) -> Response {
139 Response {
140 id,
141 result: Some(serde_json::to_value(result).unwrap()),
142 error: None,
143 }
144 }
145
146 pub fn new_err(id: RequestId, code: i32, message: String) -> Response {
148 let error = ResponseError {
149 code,
150 message,
151 data: None,
152 };
153 Response {
154 id,
155 result: None,
156 error: Some(error),
157 }
158 }
159}
160
161#[derive(Debug, Serialize, Deserialize, Clone)]
163pub struct Notification {
164 pub method: String,
166 #[serde(default = "serde_json::Value::default")]
168 #[serde(skip_serializing_if = "serde_json::Value::is_null")]
169 pub params: serde_json::Value,
170}
171
172impl Notification {
173 pub fn new(method: String, params: impl serde::Serialize) -> Notification {
175 Notification {
176 method,
177 params: serde_json::to_value(params).unwrap(),
178 }
179 }
180
181 pub fn extract<P: DeserializeOwned>(
183 self,
184 method: &str,
185 ) -> Result<P, ExtractError<Notification>> {
186 if self.method != method {
187 return Err(ExtractError::MethodMismatch(self));
188 }
189 match serde_json::from_value(self.params) {
190 Ok(params) => Ok(params),
191 Err(error) => Err(ExtractError::JsonError {
192 method: self.method,
193 error,
194 }),
195 }
196 }
197
198 #[cfg(feature = "server")]
199 pub(crate) fn is_exit(&self) -> bool {
200 self.method == "exit"
201 }
202}
203
204impl TryFrom<crate::Message> for Message {
205 type Error = anyhow::Error;
206
207 fn try_from(msg: crate::Message) -> anyhow::Result<Self> {
208 match msg {
209 crate::Message::Lsp(msg) => Ok(msg),
210 #[cfg(feature = "dap")]
211 crate::Message::Dap(msg) => anyhow::bail!("unexpected DAP message: {msg:?}"),
212 }
213 }
214}
215
216impl From<Request> for crate::Message {
217 fn from(request: Request) -> crate::Message {
218 crate::Message::Lsp(request.into())
219 }
220}
221
222impl From<Response> for crate::Message {
223 fn from(response: Response) -> crate::Message {
224 crate::Message::Lsp(response.into())
225 }
226}
227
228impl From<Notification> for crate::Message {
229 fn from(notification: Notification) -> crate::Message {
230 crate::Message::Lsp(notification.into())
231 }
232}
233
234impl From<Response> for LspOrDapResponse {
235 fn from(resp: Response) -> Self {
236 Self::Lsp(resp)
237 }
238}
239
240impl TryFrom<LspOrDapResponse> for Response {
241 type Error = anyhow::Error;
242
243 fn try_from(resp: LspOrDapResponse) -> anyhow::Result<Self> {
244 match resp {
245 LspOrDapResponse::Lsp(resp) => Ok(resp),
246 #[cfg(feature = "dap")]
247 LspOrDapResponse::Dap(_) => anyhow::bail!("unexpected DAP response"),
248 }
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::{Message, Notification, Request, RequestId};
255
256 #[test]
257 fn shutdown_with_explicit_null() {
258 let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\", \"params\": null }";
259 let msg: Message = serde_json::from_str(text).unwrap();
260
261 assert!(
262 matches!(msg, Message::Request(req) if req.id == 3.into() && req.method == "shutdown")
263 );
264 }
265
266 #[test]
267 fn shutdown_with_no_params() {
268 let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\"}";
269 let msg: Message = serde_json::from_str(text).unwrap();
270
271 assert!(
272 matches!(msg, Message::Request(req) if req.id == 3.into() && req.method == "shutdown")
273 );
274 }
275
276 #[test]
277 fn notification_with_explicit_null() {
278 let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\", \"params\": null }";
279 let msg: Message = serde_json::from_str(text).unwrap();
280
281 assert!(matches!(msg, Message::Notification(not) if not.method == "exit"));
282 }
283
284 #[test]
285 fn notification_with_no_params() {
286 let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\"}";
287 let msg: Message = serde_json::from_str(text).unwrap();
288
289 assert!(matches!(msg, Message::Notification(not) if not.method == "exit"));
290 }
291
292 #[test]
293 fn serialize_request_with_null_params() {
294 let msg = Message::Request(Request {
295 id: RequestId::from(3),
296 method: "shutdown".into(),
297 params: serde_json::Value::Null,
298 });
299 let serialized = serde_json::to_string(&msg).unwrap();
300
301 assert_eq!("{\"id\":3,\"method\":\"shutdown\"}", serialized);
302 }
303
304 #[test]
305 fn serialize_notification_with_null_params() {
306 let msg = Message::Notification(Notification {
307 method: "exit".into(),
308 params: serde_json::Value::Null,
309 });
310 let serialized = serde_json::to_string(&msg).unwrap();
311
312 assert_eq!("{\"method\":\"exit\"}", serialized);
313 }
314}