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 pub(crate) fn is_exit(&self) -> bool {
180 self.method == "exit"
181 }
182}
183
184impl From<Response> for LspOrDapResponse {
185 fn from(resp: Response) -> Self {
186 Self::Lsp(resp)
187 }
188}
189
190impl TryFrom<LspOrDapResponse> for Response {
191 type Error = anyhow::Error;
192
193 fn try_from(resp: LspOrDapResponse) -> anyhow::Result<Self> {
194 match resp {
195 LspOrDapResponse::Lsp(resp) => Ok(resp),
196 #[cfg(feature = "dap")]
197 LspOrDapResponse::Dap(_) => anyhow::bail!("unexpected DAP response"),
198 }
199 }
200}
201
202impl From<Request> for crate::Message {
203 fn from(request: Request) -> crate::Message {
204 crate::Message::Lsp(request.into())
205 }
206}
207
208impl From<Response> for crate::Message {
209 fn from(response: Response) -> crate::Message {
210 crate::Message::Lsp(response.into())
211 }
212}
213
214impl From<Notification> for crate::Message {
215 fn from(notification: Notification) -> crate::Message {
216 crate::Message::Lsp(notification.into())
217 }
218}
219
220impl TryFrom<crate::Message> for Message {
221 type Error = anyhow::Error;
222
223 fn try_from(msg: crate::Message) -> anyhow::Result<Self> {
224 match msg {
225 crate::Message::Lsp(msg) => Ok(msg),
226 #[cfg(feature = "dap")]
227 crate::Message::Dap(msg) => anyhow::bail!("unexpected DAP message: {msg:?}"),
228 }
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::{Message, Notification, Request, RequestId};
235
236 #[test]
237 fn shutdown_with_explicit_null() {
238 let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\", \"params\": null }";
239 let msg: Message = serde_json::from_str(text).unwrap();
240
241 assert!(
242 matches!(msg, Message::Request(req) if req.id == 3.into() && req.method == "shutdown")
243 );
244 }
245
246 #[test]
247 fn shutdown_with_no_params() {
248 let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\"}";
249 let msg: Message = serde_json::from_str(text).unwrap();
250
251 assert!(
252 matches!(msg, Message::Request(req) if req.id == 3.into() && req.method == "shutdown")
253 );
254 }
255
256 #[test]
257 fn notification_with_explicit_null() {
258 let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\", \"params\": null }";
259 let msg: Message = serde_json::from_str(text).unwrap();
260
261 assert!(matches!(msg, Message::Notification(not) if not.method == "exit"));
262 }
263
264 #[test]
265 fn notification_with_no_params() {
266 let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\"}";
267 let msg: Message = serde_json::from_str(text).unwrap();
268
269 assert!(matches!(msg, Message::Notification(not) if not.method == "exit"));
270 }
271
272 #[test]
273 fn serialize_request_with_null_params() {
274 let msg = Message::Request(Request {
275 id: RequestId::from(3),
276 method: "shutdown".into(),
277 params: serde_json::Value::Null,
278 });
279 let serialized = serde_json::to_string(&msg).unwrap();
280
281 assert_eq!("{\"id\":3,\"method\":\"shutdown\"}", serialized);
282 }
283
284 #[test]
285 fn serialize_notification_with_null_params() {
286 let msg = Message::Notification(Notification {
287 method: "exit".into(),
288 params: serde_json::Value::Null,
289 });
290 let serialized = serde_json::to_string(&msg).unwrap();
291
292 assert_eq!("{\"method\":\"exit\"}", serialized);
293 }
294}