use std::{any::type_name, collections::HashMap, fmt::Display};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use tracing::error;
use crate::server::lsp::LspMessage;
use super::{
base_types::LSPAny,
errors::{ErrorCode, LSPError},
};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(untagged)]
pub enum RPCMessage {
Request(RequestMessage),
Response(ResponseMessage),
Notification(NotificationMessage),
}
impl RPCMessage {
pub fn get_method(&self) -> Option<&str> {
match self {
RPCMessage::Notification(notification) => Some(¬ification.method),
RPCMessage::Request(request) => Some(&request.method),
RPCMessage::Response(_) => None,
}
}
pub fn parse<T>(&self) -> Result<T, LSPError>
where
T: DeserializeOwned,
{
match serde_json::to_string(self) {
Ok(serialized_message) => serde_json::from_str(&serialized_message).map_err(|error| {
LSPError::new(
ErrorCode::ParseError,
&format!(
"Could not deserialize RPC-message \"{}\"\n\n{}",
type_name::<T>(),
error
),
)
}),
Err(error) => Err(LSPError::new(
ErrorCode::ParseError,
&format!("Could not serialize RPC-message\n\n{}", error),
)),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Message {
pub jsonrpc: String,
}
impl Message {
pub fn new() -> Self {
Self {
jsonrpc: "2.0".to_string(),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct RecoverId {
pub id: RequestId,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct RequestMessage {
#[serde(flatten)]
pub base: Message,
pub id: RequestId,
pub method: String,
pub params: Option<Params>,
}
#[derive(Deserialize, Serialize, Debug, PartialEq)]
pub struct RequestMessageBase {
#[serde(flatten)]
pub base: Message,
pub id: RequestId,
pub method: String,
}
impl RequestMessageBase {
pub(crate) fn new(method: &str, id: u32) -> Self {
Self {
base: Message::new(),
id: RequestId::Integer(id),
method: method.to_string(),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(untagged)]
pub enum RequestId {
String(String),
Integer(u32),
}
impl Display for RequestId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RequestId::String(str) => write!(f, "{}", str),
RequestId::Integer(int) => write!(f, "{}", int),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(untagged)]
pub enum Params {
Array(Vec<serde_json::Value>),
Object(HashMap<String, serde_json::Value>),
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct ResponseMessage {
#[serde(flatten)]
pub base: Message,
pub id: RequestIdOrNull,
pub result: Option<LSPAny>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<LSPError>,
}
impl LspMessage for ResponseMessage {}
impl ResponseMessage {
pub fn error(id: &RequestId, error: LSPError) -> Self {
Self {
base: Message::new(),
id: RequestIdOrNull::RequestId(id.clone()),
result: None,
error: Some(error),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(untagged)]
pub enum RequestIdOrNull {
RequestId(RequestId),
Null,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct ResponseMessageBase {
#[serde(flatten)]
pub base: Message,
pub id: RequestIdOrNull,
}
impl ResponseMessageBase {
pub fn success(id: &RequestId) -> Self {
Self {
base: Message::new(),
id: RequestIdOrNull::RequestId(id.clone()),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct NotificationMessage {
#[serde(flatten)]
pub base: Message,
pub method: String,
pub params: Option<Params>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct NotificationMessageBase {
#[serde(flatten)]
pub base: Message,
pub method: String,
}
impl NotificationMessageBase {
pub(crate) fn new(method: &str) -> Self {
Self {
base: Message::new(),
method: method.to_string(),
}
}
}
pub fn deserialize_message(message: &str) -> Result<RPCMessage, LSPError> {
serde_json::from_str(message).map_err(|error| {
error!(
"Error while serializing message:\n{}-----------------------\n{}",
error, message,
);
LSPError::new(
ErrorCode::ParseError,
&format!("Could not serialize RPC-Message:\n\n{}", error),
)
})
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use serde_json::json;
use crate::server::lsp::{
base_types::LSPAny,
rpc::{
Message, NotificationMessage, Params, RequestId, RequestIdOrNull, RequestMessage,
ResponseMessage, deserialize_message,
},
};
use super::RPCMessage;
#[test]
fn serialize() {
let message = RPCMessage::Request(RequestMessage {
base: Message::new(),
id: RequestId::Integer(1),
method: "initialize".to_owned(),
params: Some(Params::Array(vec![])),
});
let serialized = serde_json::to_string(&message).unwrap();
assert_eq!(
serialized,
r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":[]}"#
);
}
#[test]
fn deserialize_notification() {
let maybe_initialized =
deserialize_message(r#"{"params":{"a":2},"jsonrpc":"2.0","method":"initialized"}"#)
.unwrap();
assert_eq!(
maybe_initialized,
RPCMessage::Notification(NotificationMessage {
base: Message::new(),
method: "initialized".to_owned(),
params: Some(Params::Object(HashMap::from([("a".to_string(), json!(2))])))
})
);
}
#[test]
fn deserialize_request() {
let maybe_request = deserialize_message(
r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"a":[1,2,3]}}"#,
);
assert_eq!(
maybe_request,
Ok(RPCMessage::Request(RequestMessage {
base: Message::new(),
id: RequestId::Integer(1),
method: "initialize".to_owned(),
params: Some(Params::Object(HashMap::from([(
"a".to_string(),
json!([1, 2, 3])
)])))
}))
);
}
#[test]
fn deserialize_response() {
let maybe_response = deserialize_message(r#"{"jsonrpc":"2.0","id":1,"result":{"a":1}}"#);
assert_eq!(
maybe_response,
Ok(RPCMessage::Response(ResponseMessage {
base: Message::new(),
id: RequestIdOrNull::RequestId(RequestId::Integer(1)),
error: None,
result: Some(LSPAny::LSPObject(HashMap::from([(
"a".to_string(),
LSPAny::Uinteger(1)
)])))
}))
);
}
}