tower_lsp/jsonrpc/
request.rs

1use std::borrow::Cow;
2use std::fmt::{self, Display, Formatter};
3use std::str::FromStr;
4
5use serde::{Deserialize, Deserializer, Serialize};
6use serde_json::Value;
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<Value>,
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        Request {
59            jsonrpc: Version,
60            method: R::METHOD.into(),
61            params: Some(serde_json::to_value(params).unwrap()),
62            id: Some(id),
63        }
64    }
65
66    /// Constructs a JSON-RPC notification from its corresponding LSP type.
67    ///
68    /// # Panics
69    ///
70    /// Panics if `params` could not be serialized into a [`serde_json::Value`]. Since the
71    /// [`lsp_types::notification::Notification`] trait promises this invariant is upheld, this
72    /// should never happen in practice (unless the trait was implemented incorrectly).
73    pub(crate) fn from_notification<N>(params: N::Params) -> Self
74    where
75        N: lsp_types::notification::Notification,
76    {
77        Request {
78            jsonrpc: Version,
79            method: N::METHOD.into(),
80            params: Some(serde_json::to_value(params).unwrap()),
81            id: None,
82        }
83    }
84
85    /// Returns the name of the method to be invoked.
86    pub fn method(&self) -> &str {
87        self.method.as_ref()
88    }
89
90    /// Returns the unique ID of this request, if present.
91    pub fn id(&self) -> Option<&Id> {
92        self.id.as_ref()
93    }
94
95    /// Returns the `params` field, if present.
96    pub fn params(&self) -> Option<&Value> {
97        self.params.as_ref()
98    }
99
100    /// Splits this request into the method name, request ID, and the `params` field, if present.
101    pub fn into_parts(self) -> (Cow<'static, str>, Option<Id>, Option<Value>) {
102        (self.method, self.id, self.params)
103    }
104}
105
106impl Display for Request {
107    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
108        use std::{io, str};
109
110        struct WriterFormatter<'a, 'b: 'a> {
111            inner: &'a mut Formatter<'b>,
112        }
113
114        impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> {
115            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
116                fn io_error<E>(_: E) -> io::Error {
117                    // Error value does not matter because fmt::Display impl below just
118                    // maps it to fmt::Error
119                    io::Error::new(io::ErrorKind::Other, "fmt error")
120                }
121                let s = str::from_utf8(buf).map_err(io_error)?;
122                self.inner.write_str(s).map_err(io_error)?;
123                Ok(buf.len())
124            }
125
126            fn flush(&mut self) -> io::Result<()> {
127                Ok(())
128            }
129        }
130
131        let mut w = WriterFormatter { inner: f };
132        serde_json::to_writer(&mut w, self).map_err(|_| fmt::Error)
133    }
134}
135
136impl FromStr for Request {
137    type Err = serde_json::Error;
138
139    fn from_str(s: &str) -> Result<Self, Self::Err> {
140        serde_json::from_str(s)
141    }
142}
143
144/// A builder to construct the properties of a `Request`.
145///
146/// To construct a `RequestBuilder`, refer to [`Request::build`].
147#[derive(Debug)]
148pub struct RequestBuilder {
149    method: Cow<'static, str>,
150    params: Option<Value>,
151    id: Option<Id>,
152}
153
154impl RequestBuilder {
155    /// Sets the `id` member of the request to the given value.
156    ///
157    /// If this method is not called, the resulting `Request` will be assumed to be a notification.
158    pub fn id<I: Into<Id>>(mut self, id: I) -> Self {
159        self.id = Some(id.into());
160        self
161    }
162
163    /// Sets the `params` member of the request to the given value.
164    ///
165    /// This member is omitted from the request by default.
166    pub fn params<V: Into<Value>>(mut self, params: V) -> Self {
167        self.params = Some(params.into());
168        self
169    }
170
171    /// Constructs the JSON-RPC request and returns it.
172    pub fn finish(self) -> Request {
173        Request {
174            jsonrpc: Version,
175            method: self.method,
176            params: self.params,
177            id: self.id,
178        }
179    }
180}