pub use self::error::{Error, ErrorCode};
pub use self::router::{FromParams, IntoResponse, Method};
pub(crate) use self::router::Router;
use std::borrow::Cow;
use std::fmt::{self, Debug, Display, Formatter};
use lsp_types::NumberOrString;
use serde::de::{self, Deserializer};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use serde_json::Value;
mod error;
mod router;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
#[derive(Default)]
pub enum Id {
Number(i64),
String(String),
#[default]
Null,
}
impl Display for Id {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Id::Number(id) => Display::fmt(id, f),
Id::String(id) => Debug::fmt(id, f),
Id::Null => f.write_str("null"),
}
}
}
impl From<i64> for Id {
fn from(n: i64) -> Self {
Id::Number(n)
}
}
impl From<&'_ str> for Id {
fn from(s: &'_ str) -> Self {
Id::String(s.to_string())
}
}
impl From<String> for Id {
fn from(s: String) -> Self {
Id::String(s)
}
}
impl From<NumberOrString> for Id {
fn from(num_or_str: NumberOrString) -> Self {
match num_or_str {
NumberOrString::Number(num) => Id::Number(num as i64),
NumberOrString::String(s) => Id::String(s),
}
}
}
fn deserialize_some<'de, T, D>(deserializer: D) -> std::result::Result<Option<T>, D::Error>
where
T: Deserialize<'de>,
D: Deserializer<'de>,
{
T::deserialize(deserializer).map(Some)
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Request {
jsonrpc: Version,
#[serde(default)]
method: Cow<'static, str>,
#[serde(default, deserialize_with = "deserialize_some")]
#[serde(skip_serializing_if = "Option::is_none")]
params: Option<Value>,
#[serde(default, deserialize_with = "deserialize_some")]
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<Id>,
}
impl Request {
pub fn build<M>(method: M) -> RequestBuilder
where
M: Into<Cow<'static, str>>,
{
RequestBuilder {
method: method.into(),
params: None,
id: None,
}
}
pub(crate) fn from_request<R>(id: Id, params: R::Params) -> Self
where
R: lsp_types::request::Request,
{
Request {
jsonrpc: Version,
method: R::METHOD.into(),
params: Some(serde_json::to_value(params).unwrap()),
id: Some(id),
}
}
pub(crate) fn from_notification<N>(params: N::Params) -> Self
where
N: lsp_types::notification::Notification,
{
Request {
jsonrpc: Version,
method: N::METHOD.into(),
params: Some(serde_json::to_value(params).unwrap()),
id: None,
}
}
#[inline]
pub fn method(&self) -> &str {
self.method.as_ref()
}
#[inline]
pub fn id(&self) -> Option<&Id> {
self.id.as_ref()
}
#[inline]
pub fn params(&self) -> Option<&Value> {
self.params.as_ref()
}
#[inline]
pub fn into_parts(self) -> (Cow<'static, str>, Option<Id>, Option<Value>) {
(self.method, self.id, self.params)
}
}
impl Display for Request {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut w = WriterFormatter { inner: f };
serde_json::to_writer(&mut w, self).map_err(|_| fmt::Error)
}
}
struct WriterFormatter<'a, 'b: 'a> {
inner: &'a mut Formatter<'b>,
}
impl<'a, 'b> std::io::Write for WriterFormatter<'a, 'b> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
fn io_error<E>(_: E) -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::Other, "fmt error")
}
let s = std::str::from_utf8(buf).map_err(io_error)?;
self.inner.write_str(s).map_err(io_error)?;
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[derive(Debug)]
pub struct RequestBuilder {
method: Cow<'static, str>,
params: Option<Value>,
id: Option<Id>,
}
impl RequestBuilder {
pub fn id<I: Into<Id>>(mut self, id: I) -> Self {
self.id = Some(id.into());
self
}
pub fn params<V: Into<Value>>(mut self, params: V) -> Self {
self.params = Some(params.into());
self
}
pub fn finish(self) -> Request {
Request {
jsonrpc: Version,
method: self.method,
params: self.params,
id: self.id,
}
}
}
#[derive(Clone, PartialEq, Deserialize, Serialize)]
pub struct Response {
jsonrpc: Version,
#[serde(flatten)]
kind: ResponseKind,
id: Id,
}
impl Response {
#[inline]
pub const fn from_ok(id: Id, result: Value) -> Self {
Response {
jsonrpc: Version,
kind: ResponseKind::Ok { result },
id,
}
}
#[inline]
pub const fn from_error(id: Id, error: Error) -> Self {
Response {
jsonrpc: Version,
kind: ResponseKind::Err { error },
id,
}
}
pub fn from_parts(id: Id, body: Result<Value>) -> Self {
match body {
Ok(result) => Response::from_ok(id, result),
Err(error) => Response::from_error(id, error),
}
}
pub fn into_parts(self) -> (Id, Result<Value>) {
match self.kind {
ResponseKind::Ok { result } => (self.id, Ok(result)),
ResponseKind::Err { error } => (self.id, Err(error)),
}
}
#[inline]
pub const fn is_ok(&self) -> bool {
matches!(self.kind, ResponseKind::Ok { .. })
}
#[inline]
pub const fn is_error(&self) -> bool {
!self.is_ok()
}
#[inline]
pub const fn result(&self) -> Option<&Value> {
match &self.kind {
ResponseKind::Ok { result } => Some(result),
_ => None,
}
}
#[inline]
pub const fn error(&self) -> Option<&Error> {
match &self.kind {
ResponseKind::Err { error } => Some(error),
_ => None,
}
}
#[inline]
pub const fn id(&self) -> &Id {
&self.id
}
}
impl Debug for Response {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut d = f.debug_struct("Response");
d.field("jsonrpc", &self.jsonrpc);
match &self.kind {
ResponseKind::Ok { result } => d.field("result", result),
ResponseKind::Err { error } => d.field("error", error),
};
d.field("id", &self.id).finish()
}
}
#[derive(Clone, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
enum ResponseKind {
Ok { result: Value },
Err { error: Error },
}
#[derive(Deserialize, Serialize)]
#[cfg_attr(test, derive(Debug, PartialEq))]
#[serde(untagged)]
pub(crate) enum Message {
Response(Response),
Request(Request),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct Version;
impl<'de> Deserialize<'de> for Version {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
match Cow::<'de, str>::deserialize(deserializer)?.as_ref() {
"2.0" => Ok(Version),
_ => Err(de::Error::custom("expected JSON-RPC version \"2.0\"")),
}
}
}
impl Serialize for Version {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
"2.0".serialize(serializer)
}
}
pub(crate) fn not_initialized_error() -> Error {
Error {
code: ErrorCode::ServerError(-32002),
message: "Server not initialized".to_string(),
data: None,
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
#[test]
fn incoming_from_str_or_value() {
let v = json!({"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{}},"id":0});
let from_str: Message = serde_json::from_str(&v.to_string()).unwrap();
let from_value: Message = serde_json::from_value(v).unwrap();
assert_eq!(from_str, from_value);
}
#[test]
fn outgoing_from_str_or_value() {
let v = json!({"jsonrpc":"2.0","result":{},"id":1});
let from_str: Message = serde_json::from_str(&v.to_string()).unwrap();
let from_value: Message = serde_json::from_value(v).unwrap();
assert_eq!(from_str, from_value);
}
#[test]
fn parses_incoming_message() {
let server_request =
json!({"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{}},"id":0});
let incoming = serde_json::from_value(server_request).unwrap();
assert!(matches!(incoming, Message::Request(_)));
let server_notif = json!({"jsonrpc":"2.0","method":"initialized","params":{}});
let incoming = serde_json::from_value(server_notif).unwrap();
assert!(matches!(incoming, Message::Request(_)));
let client_request = json!({"jsonrpc":"2.0","id":0,"result":[null]});
let incoming = serde_json::from_value(client_request).unwrap();
assert!(matches!(incoming, Message::Response(_)));
}
#[test]
fn parses_outgoing_message() {
let client_request = json!({"jsonrpc":"2.0","method":"workspace/configuration","params":{"scopeUri":null,"section":"foo"},"id":0});
let outgoing = serde_json::from_value(client_request).unwrap();
assert!(matches!(outgoing, Message::Request(_)));
let client_notif = json!({"jsonrpc":"2.0","method":"window/logMessage","params":{"message":"foo","type":0}});
let outgoing = serde_json::from_value(client_notif).unwrap();
assert!(matches!(outgoing, Message::Request(_)));
let server_response = json!({"jsonrpc":"2.0","id":0,"result":[null]});
let outgoing = serde_json::from_value(server_response).unwrap();
assert!(matches!(outgoing, Message::Response(_)));
}
#[test]
fn parses_invalid_server_request() {
let unknown_method = json!({"jsonrpc":"2.0","method":"foo"});
let incoming = serde_json::from_value(unknown_method).unwrap();
assert!(matches!(incoming, Message::Request(_)));
let unknown_method_with_id = json!({"jsonrpc":"2.0","method":"foo","id":0});
let incoming = serde_json::from_value(unknown_method_with_id).unwrap();
assert!(matches!(incoming, Message::Request(_)));
let missing_method = json!({"jsonrpc":"2.0"});
let incoming = serde_json::from_value(missing_method).unwrap();
assert!(matches!(incoming, Message::Request(_)));
let missing_method_with_id = json!({"jsonrpc":"2.0","id":0});
let incoming = serde_json::from_value(missing_method_with_id).unwrap();
assert!(matches!(incoming, Message::Request(_)));
}
#[test]
fn accepts_null_request_id() {
let request_id: Id = serde_json::from_value(json!(null)).unwrap();
assert_eq!(request_id, Id::Null);
}
#[test]
fn accepts_negative_integer_request_id() {
let request_id: Id = serde_json::from_value(json!(-1)).unwrap();
assert_eq!(request_id, Id::Number(-1));
}
}