use {
crate::protocol::lsp::NumberOrString,
serde::{
Deserialize,
Serialize,
de::{
self,
Deserializer,
},
ser::Serializer,
},
std::{
borrow::Cow,
fmt::{
self,
Debug,
Display,
Formatter,
},
},
};
pub use self::{
error::{
Error,
ErrorCode,
Result,
},
message::Message,
notification::{
Notification,
NotificationBuilder,
},
request::{
Request,
RequestBuilder,
},
response::Response,
};
pub mod error;
pub mod message;
pub mod notification;
pub mod request;
pub mod response;
#[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 {
| Self::Number(id) => Display::fmt(id, f),
| Self::String(id) => Debug::fmt(id, f),
| Self::Null => f.write_str("null"),
}
}
}
impl From<i64> for Id {
fn from(n: i64) -> Self {
Self::Number(n)
}
}
impl From<&'_ str> for Id {
fn from(s: &'_ str) -> Self {
Self::String(s.to_string())
}
}
impl From<String> for Id {
fn from(s: String) -> Self {
Self::String(s)
}
}
impl From<NumberOrString> for Id {
fn from(num_or_str: NumberOrString) -> Self {
match num_or_str {
| NumberOrString::Number(num) => Self::Number(i64::from(num)),
| NumberOrString::String(s) => Self::String(s),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Version;
impl<'de> Deserialize<'de> for Version {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Inner<'a>(#[serde(borrow)] Cow<'a, str>);
let Inner(ver) = Inner::deserialize(deserializer)?;
match ver.as_ref() {
| "2.0" => Ok(Self),
| _ => 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,
{
serializer.serialize_str("2.0")
}
}
#[cfg(test)]
mod tests {
use {
super::*,
serde_json::json,
};
#[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::Notification(_)));
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::Notification(_)));
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::Notification(_)));
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: std::result::Result<Message, _> =
serde_json::from_value(missing_method);
assert!(incoming.is_err());
let missing_method_with_id = json!({"jsonrpc":"2.0","id":0});
let incoming: std::result::Result<Message, _> =
serde_json::from_value(missing_method_with_id);
assert!(incoming.is_err());
}
#[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));
}
#[test]
fn message_request_round_trip_preserves_trace_context() {
let request = Request::build("test/method", 1)
.params(json!({"key": "value"}))
.traceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
.tracestate("rojo=00f067aa0ba902b7,congo=t61rcWkgMzE")
.finish();
let message = Message::Request(request);
let serialized = serde_json::to_string(&message).unwrap();
let deserialized: Message = serde_json::from_str(&serialized).unwrap();
match deserialized {
| Message::Request(r) => {
assert_eq!(r.method(), "test/method");
assert_eq!(
r.traceparent(),
Some("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
);
assert_eq!(
r.tracestate(),
Some("rojo=00f067aa0ba902b7,congo=t61rcWkgMzE")
);
},
| _ => panic!("Expected Message::Request"),
}
}
#[test]
fn message_notification_round_trip_preserves_trace_context() {
let notification = Notification::build("test/notification")
.params(json!({"data": 123}))
.traceparent("00-abcdef1234567890abcdef1234567890-1234567890abcdef-01")
.tracestate("vendor=state")
.finish();
let message = Message::Notification(notification);
let serialized = serde_json::to_string(&message).unwrap();
let deserialized: Message = serde_json::from_str(&serialized).unwrap();
match deserialized {
| Message::Notification(n) => {
assert_eq!(n.method(), "test/notification");
assert_eq!(
n.traceparent(),
Some("00-abcdef1234567890abcdef1234567890-1234567890abcdef-01")
);
assert_eq!(n.tracestate(), Some("vendor=state"));
},
| _ => panic!("Expected Message::Notification"),
}
}
#[test]
fn message_parses_request_with_trace_context() {
let json_with_trace = json!({
"jsonrpc": "2.0",
"method": "textDocument/hover",
"params": {"textDocument": {"uri": "file:///test.rs"}, "position": {"line": 0, "character": 0}},
"id": 42,
"traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
"tracestate": "rojo=00f067aa0ba902b7"
});
let message: Message = serde_json::from_value(json_with_trace).unwrap();
match message {
| Message::Request(r) => {
assert_eq!(r.method(), "textDocument/hover");
assert_eq!(r.id(), &Id::Number(42));
assert_eq!(
r.traceparent(),
Some("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
);
assert_eq!(r.tracestate(), Some("rojo=00f067aa0ba902b7"));
},
| _ => panic!("Expected Message::Request"),
}
}
#[test]
fn message_parses_notification_with_trace_context() {
let json_with_trace = json!({
"jsonrpc": "2.0",
"method": "textDocument/didOpen",
"params": {"textDocument": {"uri": "file:///test.rs", "languageId": "rust", "version": 1, "text": ""}},
"traceparent": "00-1234567890abcdef1234567890abcdef-abcdef1234567890-01"
});
let message: Message = serde_json::from_value(json_with_trace).unwrap();
match message {
| Message::Notification(n) => {
assert_eq!(n.method(), "textDocument/didOpen");
assert_eq!(
n.traceparent(),
Some("00-1234567890abcdef1234567890abcdef-abcdef1234567890-01")
);
assert_eq!(n.tracestate(), None);
},
| _ => panic!("Expected Message::Notification"),
}
}
#[test]
fn message_backward_compatible_without_trace_context() {
let request_json = json!({
"jsonrpc": "2.0",
"method": "initialize",
"params": {"capabilities": {}},
"id": 0
});
let message: Message = serde_json::from_value(request_json).unwrap();
match message {
| Message::Request(r) => {
assert_eq!(r.traceparent(), None);
assert_eq!(r.tracestate(), None);
},
| _ => panic!("Expected Message::Request"),
}
let notif_json = json!({
"jsonrpc": "2.0",
"method": "initialized",
"params": {}
});
let message: Message = serde_json::from_value(notif_json).unwrap();
match message {
| Message::Notification(n) => {
assert_eq!(n.traceparent(), None);
assert_eq!(n.tracestate(), None);
},
| _ => panic!("Expected Message::Notification"),
}
}
}