tower_lsp_server/jsonrpc/
request.rs

1use std::borrow::Cow;
2use std::fmt::{self, Display, Formatter};
3use std::str::FromStr;
4
5use lsp_types::LSPAny;
6use serde::{Deserialize, Deserializer, Serialize};
7
8use super::{Id, Version};
9
10fn deserialize_some<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
11where
12    T: Deserialize<'de>,
13    D: Deserializer<'de>,
14{
15    T::deserialize(deserializer).map(Some)
16}
17
18/// A JSON-RPC request or notification.
19#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
20pub struct Request {
21    jsonrpc: Version,
22    #[serde(default)]
23    method: Cow<'static, str>,
24    #[serde(default, deserialize_with = "deserialize_some")]
25    #[serde(skip_serializing_if = "Option::is_none")]
26    params: Option<LSPAny>,
27    #[serde(default, deserialize_with = "deserialize_some")]
28    #[serde(skip_serializing_if = "Option::is_none")]
29    id: Option<Id>,
30}
31
32impl Request {
33    /// Starts building a JSON-RPC method call.
34    ///
35    /// Returns a `RequestBuilder`, which allows setting the `params` field or adding a request ID.
36    pub fn build<M>(method: M) -> RequestBuilder
37    where
38        M: Into<Cow<'static, str>>,
39    {
40        RequestBuilder {
41            method: method.into(),
42            params: None,
43            id: None,
44        }
45    }
46
47    /// Constructs a JSON-RPC request from its corresponding LSP type.
48    ///
49    /// # Panics
50    ///
51    /// Panics if `params` could not be serialized into a [`serde_json::Value`]. Since the
52    /// [`lsp_types::request::Request`] trait promises this invariant is upheld, this should never
53    /// happen in practice (unless the trait was implemented incorrectly).
54    pub(crate) fn from_request<R>(id: Id, params: R::Params) -> Self
55    where
56        R: lsp_types::request::Request,
57    {
58        let params = serde_json::to_value(params).expect("request params cannot be serialized");
59
60        Self {
61            jsonrpc: Version,
62            method: R::METHOD.into(),
63            params: Some(params),
64            id: Some(id),
65        }
66    }
67
68    /// Constructs a JSON-RPC notification from its corresponding LSP type.
69    ///
70    /// # Panics
71    ///
72    /// Panics if `params` could not be serialized into a [`serde_json::Value`]. Since the
73    /// [`lsp_types::notification::Notification`] trait promises this invariant is upheld, this
74    /// should never happen in practice (unless the trait was implemented incorrectly).
75    pub(crate) fn from_notification<N>(params: N::Params) -> Self
76    where
77        N: lsp_types::notification::Notification,
78    {
79        let params =
80            serde_json::to_value(params).expect("notification params cannot be serialized");
81
82        Self {
83            jsonrpc: Version,
84            method: N::METHOD.into(),
85            params: Some(params),
86            id: None,
87        }
88    }
89
90    /// Returns the name of the method to be invoked.
91    #[must_use]
92    pub fn method(&self) -> &str {
93        self.method.as_ref()
94    }
95
96    /// Returns the unique ID of this request, if present.
97    #[must_use]
98    pub const fn id(&self) -> Option<&Id> {
99        self.id.as_ref()
100    }
101
102    /// Returns the `params` field, if present.
103    #[must_use]
104    pub const fn params(&self) -> Option<&LSPAny> {
105        self.params.as_ref()
106    }
107
108    /// Splits this request into the method name, request ID, and the `params` field, if present.
109    #[must_use]
110    pub fn into_parts(self) -> (Cow<'static, str>, Option<Id>, Option<LSPAny>) {
111        (self.method, self.id, self.params)
112    }
113}
114
115impl Display for Request {
116    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
117        use std::{io, str};
118
119        struct WriterFormatter<'a, 'b: 'a> {
120            inner: &'a mut Formatter<'b>,
121        }
122
123        impl io::Write for WriterFormatter<'_, '_> {
124            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
125                fn io_error<E>(_: E) -> io::Error {
126                    // Error value does not matter because fmt::Display impl below just
127                    // maps it to fmt::Error
128                    io::Error::other("fmt error")
129                }
130                let s = str::from_utf8(buf).map_err(io_error)?;
131                self.inner.write_str(s).map_err(io_error)?;
132                Ok(buf.len())
133            }
134
135            fn flush(&mut self) -> io::Result<()> {
136                Ok(())
137            }
138        }
139
140        let mut w = WriterFormatter { inner: f };
141        serde_json::to_writer(&mut w, self).map_err(|_| fmt::Error)
142    }
143}
144
145impl FromStr for Request {
146    type Err = serde_json::Error;
147
148    fn from_str(s: &str) -> Result<Self, Self::Err> {
149        serde_json::from_str(s)
150    }
151}
152
153/// A builder to construct the properties of a `Request`.
154///
155/// To construct a `RequestBuilder`, refer to [`Request::build`].
156#[derive(Debug)]
157pub struct RequestBuilder {
158    method: Cow<'static, str>,
159    params: Option<LSPAny>,
160    id: Option<Id>,
161}
162
163impl RequestBuilder {
164    /// Sets the `id` member of the request to the given value.
165    ///
166    /// If this method is not called, the resulting `Request` will be assumed to be a notification.
167    #[must_use]
168    pub fn id<I: Into<Id>>(mut self, id: I) -> Self {
169        self.id = Some(id.into());
170        self
171    }
172
173    /// Sets the `params` member of the request to the given value.
174    ///
175    /// This member is omitted from the request by default.
176    #[must_use]
177    pub fn params<V: Into<LSPAny>>(mut self, params: V) -> Self {
178        self.params = Some(params.into());
179        self
180    }
181
182    /// Constructs the JSON-RPC request and returns it.
183    #[must_use]
184    pub fn finish(self) -> Request {
185        Request {
186            jsonrpc: Version,
187            method: self.method,
188            params: self.params,
189            id: self.id,
190        }
191    }
192}