sync_ls/
msg.rs

1//! Message from and to language servers and clients.
2
3use std::fmt;
4#[cfg(any(feature = "lsp", feature = "dap"))]
5use std::io::{self, BufRead, Write};
6
7use serde::{Deserialize, Serialize};
8
9#[cfg(feature = "dap")]
10use crate::dap;
11#[cfg(feature = "lsp")]
12use crate::lsp;
13
14/// A request ID in the Language Server Protocol.
15#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16#[serde(transparent)]
17pub struct RequestId(IdRepr);
18
19impl RequestId {
20    #[cfg(all(feature = "dap", feature = "server"))]
21    pub(crate) fn dap(id: RequestId) -> i64 {
22        match id.0 {
23            IdRepr::I32(it) => it as i64,
24            IdRepr::String(it) => panic!("unexpected string ID in DAP: {it}"),
25        }
26    }
27}
28
29#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30#[serde(untagged)]
31enum IdRepr {
32    I32(i32),
33    String(String),
34}
35
36impl From<i32> for RequestId {
37    fn from(id: i32) -> RequestId {
38        RequestId(IdRepr::I32(id))
39    }
40}
41
42impl From<String> for RequestId {
43    fn from(id: String) -> RequestId {
44        RequestId(IdRepr::String(id))
45    }
46}
47
48impl fmt::Display for RequestId {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match &self.0 {
51            IdRepr::I32(it) => fmt::Display::fmt(it, f),
52            // Use debug here, to make it clear that `92` and `"92"` are
53            // different, and to reduce WTF factor if the sever uses `" "` as an
54            // ID.
55            IdRepr::String(it) => fmt::Debug::fmt(it, f),
56        }
57    }
58}
59
60/// A response from the server.
61#[derive(Debug, Serialize, Deserialize, Clone)]
62pub struct ResponseError {
63    /// The error code.
64    pub code: i32,
65    /// The error message.
66    pub message: String,
67    /// Additional data.
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub data: Option<serde_json::Value>,
70}
71
72/// The error codes defined by the JSON RPC.
73#[derive(Clone, Copy, Debug)]
74#[non_exhaustive]
75pub enum ErrorCode {
76    // Defined by JSON RPC:
77    /// Invalid JSON was received by the server.
78    ParseError = -32700,
79    /// The JSON sent is not a valid Request object.
80    InvalidRequest = -32600,
81    /// The method does not exist / is not available.
82    MethodNotFound = -32601,
83    /// Invalid method parameter(s).
84    InvalidParams = -32602,
85    /// Internal JSON-RPC error.
86    InternalError = -32603,
87    /// The JSON sent is not a valid Request object.
88    ServerErrorStart = -32099,
89    /// The JSON sent is not a valid Request object.
90    ServerErrorEnd = -32000,
91
92    /// Error code indicating that a server received a notification or
93    /// request before the server has received the `initialize` request.
94    ServerNotInitialized = -32002,
95    /// Error code indicating that a server received a request that
96    /// is missing a required property.
97    UnknownErrorCode = -32001,
98
99    // Defined by the protocol:
100    /// The client has canceled a request and a server has detected
101    /// the cancel.
102    RequestCanceled = -32800,
103
104    /// The server detected that the content of a document got
105    /// modified outside normal conditions. A server should
106    /// NOT send this error code if it detects a content change
107    /// in it unprocessed messages. The result even computed
108    /// on an older state might still be useful for the client.
109    ///
110    /// If a client decides that a result is not of any use anymore
111    /// the client should cancel the request.
112    ContentModified = -32801,
113
114    /// The server cancelled the request. This error code should
115    /// only be used for requests that explicitly support being
116    /// server cancellable.
117    ///
118    /// @since 3.17.0
119    ServerCancelled = -32802,
120
121    /// A request failed but it was syntactically correct, e.g the
122    /// method name was known and the parameters were valid. The error
123    /// message should contain human readable information about why
124    /// the request failed.
125    ///
126    /// @since 3.17.0
127    RequestFailed = -32803,
128}
129
130/// The common message type for the LSP protocol.
131#[cfg(feature = "lsp")]
132pub type LspMessage = lsp::Message;
133/// The common message type for the DAP protocol.
134#[cfg(feature = "dap")]
135pub type DapMessage = dap::Message;
136
137/// The common message type for the language server.
138#[derive(Debug)]
139pub enum Message {
140    /// A message in the LSP protocol.
141    #[cfg(feature = "lsp")]
142    Lsp(LspMessage),
143    /// A message in the DAP protocol.
144    #[cfg(feature = "dap")]
145    Dap(DapMessage),
146}
147
148impl Message {
149    /// Reads a lsp message from the given reader.
150    #[cfg(feature = "lsp")]
151    pub fn read_lsp<R: std::io::BufRead>(reader: &mut R) -> std::io::Result<Option<Self>> {
152        let msg = lsp::Message::read(reader)?;
153        Ok(msg.map(Message::Lsp))
154    }
155
156    /// Reads a dap message from the given reader.
157    #[cfg(feature = "dap")]
158    pub fn read_dap<R: std::io::BufRead>(reader: &mut R) -> std::io::Result<Option<Self>> {
159        let msg = dap::Message::read(reader)?;
160        Ok(msg.map(Message::Dap))
161    }
162
163    /// Writes the message to the given writer.
164    pub fn write<W: std::io::Write>(self, _writer: &mut W) -> std::io::Result<()> {
165        match self {
166            #[cfg(feature = "lsp")]
167            Message::Lsp(msg) => msg.write(_writer),
168            #[cfg(feature = "dap")]
169            Message::Dap(msg) => msg.write(_writer),
170        }
171    }
172}
173
174/// The kind of the message.
175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
176pub enum MessageKind {
177    /// A message in the LSP protocol.
178    #[cfg(feature = "lsp")]
179    Lsp,
180    /// A message in the DAP protocol.
181    #[cfg(feature = "dap")]
182    Dap,
183}
184
185/// Gets the kind of the message.
186pub trait GetMessageKind {
187    /// Returns the kind of the message.
188    const MESSAGE_KIND: MessageKind;
189}
190
191#[cfg(feature = "lsp")]
192impl GetMessageKind for LspMessage {
193    const MESSAGE_KIND: MessageKind = MessageKind::Lsp;
194}
195
196#[cfg(feature = "dap")]
197impl GetMessageKind for DapMessage {
198    const MESSAGE_KIND: MessageKind = MessageKind::Dap;
199}
200
201#[allow(unused)]
202pub(crate) enum LspOrDapResponse {
203    #[cfg(feature = "lsp")]
204    Lsp(lsp::Response),
205    #[cfg(feature = "dap")]
206    Dap(dap::Response),
207}
208
209#[cfg(any(feature = "lsp", feature = "dap"))]
210pub(crate) fn read_msg_text(inp: &mut dyn BufRead) -> io::Result<Option<String>> {
211    let mut size = None;
212    let mut buf = String::new();
213    loop {
214        buf.clear();
215        if inp.read_line(&mut buf)? == 0 {
216            return Ok(None);
217        }
218        if !buf.ends_with("\r\n") {
219            return Err(invalid_data_fmt!("malformed header: {buf:?}"));
220        }
221        let buf = &buf[..buf.len() - 2];
222        if buf.is_empty() {
223            break;
224        }
225        let mut parts = buf.splitn(2, ": ");
226        let header_name = parts.next().unwrap();
227        let header_value = parts
228            .next()
229            .ok_or_else(|| invalid_data_fmt!("malformed header: {buf:?}"))?;
230        if header_name.eq_ignore_ascii_case("Content-Length") {
231            size = Some(header_value.parse::<usize>().map_err(invalid_data)?);
232        }
233    }
234    let size: usize = size.ok_or_else(|| invalid_data_fmt!("no Content-Length"))?;
235    let mut buf = buf.into_bytes();
236    buf.resize(size, 0);
237    inp.read_exact(&mut buf)?;
238    let buf = String::from_utf8(buf).map_err(invalid_data)?;
239    log::debug!("< {buf}");
240    Ok(Some(buf))
241}
242
243#[cfg(any(feature = "lsp", feature = "dap"))]
244pub(crate) fn write_msg_text(out: &mut dyn Write, msg: &str) -> io::Result<()> {
245    log::debug!("> {msg}");
246    write!(out, "Content-Length: {}\r\n\r\n", msg.len())?;
247    out.write_all(msg.as_bytes())?;
248    out.flush()?;
249    Ok(())
250}
251
252#[cfg(any(feature = "lsp", feature = "dap"))]
253pub(crate) fn invalid_data(
254    error: impl Into<Box<dyn std::error::Error + Send + Sync>>,
255) -> io::Error {
256    io::Error::new(io::ErrorKind::InvalidData, error)
257}
258
259#[cfg(any(feature = "lsp", feature = "dap"))]
260macro_rules! invalid_data_fmt {
261    ($($tt:tt)*) => ($crate::invalid_data(format!($($tt)*)))
262}
263#[cfg(any(feature = "lsp", feature = "dap"))]
264pub(crate) use invalid_data_fmt;