1#![allow(missing_docs)]
2
3use 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
43#[derive(Debug, Serialize, Deserialize, Clone)]
45pub struct Request {
46 pub id: RequestId,
47 pub method: String,
48 #[serde(default = "serde_json::Value::default")]
49 #[serde(skip_serializing_if = "serde_json::Value::is_null")]
50 pub params: serde_json::Value,
51}
52
53#[derive(Debug, Serialize, Deserialize, Clone)]
54pub struct Response {
55 pub id: RequestId,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub result: Option<serde_json::Value>,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub error: Option<ResponseError>,
63}
64
65#[derive(Debug, Serialize, Deserialize, Clone)]
66pub struct Notification {
67 pub method: String,
68 #[serde(default = "serde_json::Value::default")]
69 #[serde(skip_serializing_if = "serde_json::Value::is_null")]
70 pub params: serde_json::Value,
71}
72
73impl Message {
74 pub fn read(r: &mut impl BufRead) -> io::Result<Option<Message>> {
75 Message::_read(r)
76 }
77 fn _read(r: &mut dyn BufRead) -> io::Result<Option<Message>> {
78 let text = match read_msg_text(r)? {
79 None => return Ok(None),
80 Some(text) => text,
81 };
82
83 let msg = match serde_json::from_str(&text) {
84 Ok(msg) => msg,
85 Err(e) => {
86 return Err(invalid_data_fmt!("malformed LSP payload: {e:?}"));
87 }
88 };
89
90 Ok(Some(msg))
91 }
92 pub fn write(self, w: &mut impl Write) -> io::Result<()> {
93 self._write(w)
94 }
95 fn _write(self, w: &mut dyn Write) -> io::Result<()> {
96 #[derive(Serialize)]
97 struct JsonRpc {
98 jsonrpc: &'static str,
99 #[serde(flatten)]
100 msg: Message,
101 }
102 let text = serde_json::to_string(&JsonRpc {
103 jsonrpc: "2.0",
104 msg: self,
105 })?;
106 write_msg_text(w, &text)
107 }
108}
109
110impl Response {
111 pub fn new_ok<R: serde::Serialize>(id: RequestId, result: R) -> Response {
112 Response {
113 id,
114 result: Some(serde_json::to_value(result).unwrap()),
115 error: None,
116 }
117 }
118 pub fn new_err(id: RequestId, code: i32, message: String) -> Response {
119 let error = ResponseError {
120 code,
121 message,
122 data: None,
123 };
124 Response {
125 id,
126 result: None,
127 error: Some(error),
128 }
129 }
130}
131
132impl Request {
133 pub fn new<P: serde::Serialize>(id: RequestId, method: String, params: P) -> Request {
134 Request {
135 id,
136 method,
137 params: serde_json::to_value(params).unwrap(),
138 }
139 }
140 pub fn extract<P: DeserializeOwned>(
141 self,
142 method: &str,
143 ) -> Result<(RequestId, P), ExtractError<Request>> {
144 if self.method != method {
145 return Err(ExtractError::MethodMismatch(self));
146 }
147 match serde_json::from_value(self.params) {
148 Ok(params) => Ok((self.id, params)),
149 Err(error) => Err(ExtractError::JsonError {
150 method: self.method,
151 error,
152 }),
153 }
154 }
155}
156
157impl Notification {
158 pub fn new(method: String, params: impl serde::Serialize) -> Notification {
159 Notification {
160 method,
161 params: serde_json::to_value(params).unwrap(),
162 }
163 }
164 pub fn extract<P: DeserializeOwned>(
165 self,
166 method: &str,
167 ) -> Result<P, ExtractError<Notification>> {
168 if self.method != method {
169 return Err(ExtractError::MethodMismatch(self));
170 }
171 match serde_json::from_value(self.params) {
172 Ok(params) => Ok(params),
173 Err(error) => Err(ExtractError::JsonError {
174 method: self.method,
175 error,
176 }),
177 }
178 }
179
180 #[cfg(feature = "server")]
181 pub(crate) fn is_exit(&self) -> bool {
182 self.method == "exit"
183 }
184}
185
186impl From<Response> for LspOrDapResponse {
187 fn from(resp: Response) -> Self {
188 Self::Lsp(resp)
189 }
190}
191
192impl TryFrom<LspOrDapResponse> for Response {
193 type Error = anyhow::Error;
194
195 fn try_from(resp: LspOrDapResponse) -> anyhow::Result<Self> {
196 match resp {
197 LspOrDapResponse::Lsp(resp) => Ok(resp),
198 #[cfg(feature = "dap")]
199 LspOrDapResponse::Dap(_) => anyhow::bail!("unexpected DAP response"),
200 }
201 }
202}
203
204impl From<Request> for crate::Message {
205 fn from(request: Request) -> crate::Message {
206 crate::Message::Lsp(request.into())
207 }
208}
209
210impl From<Response> for crate::Message {
211 fn from(response: Response) -> crate::Message {
212 crate::Message::Lsp(response.into())
213 }
214}
215
216impl From<Notification> for crate::Message {
217 fn from(notification: Notification) -> crate::Message {
218 crate::Message::Lsp(notification.into())
219 }
220}
221
222impl TryFrom<crate::Message> for Message {
223 type Error = anyhow::Error;
224
225 fn try_from(msg: crate::Message) -> anyhow::Result<Self> {
226 match msg {
227 crate::Message::Lsp(msg) => Ok(msg),
228 #[cfg(feature = "dap")]
229 crate::Message::Dap(msg) => anyhow::bail!("unexpected DAP message: {msg:?}"),
230 }
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::{Message, Notification, Request, RequestId};
237
238 #[test]
239 fn shutdown_with_explicit_null() {
240 let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\", \"params\": null }";
241 let msg: Message = serde_json::from_str(text).unwrap();
242
243 assert!(
244 matches!(msg, Message::Request(req) if req.id == 3.into() && req.method == "shutdown")
245 );
246 }
247
248 #[test]
249 fn shutdown_with_no_params() {
250 let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\"}";
251 let msg: Message = serde_json::from_str(text).unwrap();
252
253 assert!(
254 matches!(msg, Message::Request(req) if req.id == 3.into() && req.method == "shutdown")
255 );
256 }
257
258 #[test]
259 fn notification_with_explicit_null() {
260 let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\", \"params\": null }";
261 let msg: Message = serde_json::from_str(text).unwrap();
262
263 assert!(matches!(msg, Message::Notification(not) if not.method == "exit"));
264 }
265
266 #[test]
267 fn notification_with_no_params() {
268 let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\"}";
269 let msg: Message = serde_json::from_str(text).unwrap();
270
271 assert!(matches!(msg, Message::Notification(not) if not.method == "exit"));
272 }
273
274 #[test]
275 fn serialize_request_with_null_params() {
276 let msg = Message::Request(Request {
277 id: RequestId::from(3),
278 method: "shutdown".into(),
279 params: serde_json::Value::Null,
280 });
281 let serialized = serde_json::to_string(&msg).unwrap();
282
283 assert_eq!("{\"id\":3,\"method\":\"shutdown\"}", serialized);
284 }
285
286 #[test]
287 fn serialize_notification_with_null_params() {
288 let msg = Message::Notification(Notification {
289 method: "exit".into(),
290 params: serde_json::Value::Null,
291 });
292 let serialized = serde_json::to_string(&msg).unwrap();
293
294 assert_eq!("{\"method\":\"exit\"}", serialized);
295 }
296}