use std::borrow::Cow;
use lsp_types_max::{
NotificationMessage, NumberOrString, RequestMessage, ResponseError, ResponseMessage,
};
use serde_json::Value;
use super::{Error, ErrorCode, Id, Request, Response};
pub trait IntoTower {
type Target;
fn into_tower(self) -> Self::Target;
}
pub trait IntoLsp {
type Target;
fn into_lsp(self) -> Self::Target;
}
impl IntoTower for RequestMessage {
type Target = Request;
fn into_tower(self) -> Self::Target {
Request::build(self.method)
.id(Id::from(self.id))
.params(self.params.unwrap_or(Value::Null))
.finish()
}
}
impl IntoTower for NotificationMessage {
type Target = Request;
fn into_tower(self) -> Self::Target {
Request::build(self.method)
.params(self.params.unwrap_or(Value::Null))
.finish()
}
}
impl IntoTower for ResponseError {
type Target = Error;
fn into_tower(self) -> Self::Target {
Error {
code: ErrorCode::from(self.code as i64),
message: Cow::Owned(self.message),
data: self.data,
}
}
}
impl IntoTower for ResponseMessage {
type Target = Response;
fn into_tower(self) -> Self::Target {
match self {
ResponseMessage::Success { id, result, .. } => {
Response::from_ok(id.map(Id::from).unwrap_or(Id::Null), result)
}
ResponseMessage::Error { id, error, .. } => {
Response::from_error(id.map(Id::from).unwrap_or(Id::Null), error.into_tower())
}
}
}
}
impl IntoLsp for Error {
type Target = ResponseError;
fn into_lsp(self) -> Self::Target {
ResponseError {
code: self.code.code() as i32,
message: self.message.into_owned(),
data: self.data,
}
}
}
impl IntoLsp for Response {
type Target = ResponseMessage;
fn into_lsp(self) -> Self::Target {
let (id, result) = self.into_parts();
match result {
Ok(result) => ResponseMessage::Success {
jsonrpc: "2.0".to_string(),
id: tower_id_to_lsp(id),
result,
},
Err(error) => ResponseMessage::Error {
jsonrpc: "2.0".to_string(),
id: tower_id_to_lsp(id),
error: error.into_lsp(),
},
}
}
}
fn tower_id_to_lsp(id: Id) -> Option<NumberOrString> {
match id {
Id::Number(n) => Some(NumberOrString::Number(n as i32)),
Id::String(s) => Some(NumberOrString::String(s)),
Id::Null => None,
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum BaseProtocolMessage {
Request(RequestMessage),
Notification(NotificationMessage),
Response(ResponseMessage),
}
impl BaseProtocolMessage {
pub fn from_request(request: Request) -> Self {
let (method, id, params) = request.into_parts();
if let Some(id) = id {
BaseProtocolMessage::Request(RequestMessage {
jsonrpc: "2.0".to_string(),
id: match id {
Id::Number(n) => NumberOrString::Number(n as i32),
Id::String(s) => NumberOrString::String(s),
Id::Null => NumberOrString::Number(0), },
method: method.into_owned(),
params,
})
} else {
BaseProtocolMessage::Notification(NotificationMessage {
jsonrpc: "2.0".to_string(),
method: method.into_owned(),
params,
})
}
}
pub fn from_response(response: Response) -> Self {
BaseProtocolMessage::Response(response.into_lsp())
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_request_message_bridge() {
let lsp_req = RequestMessage {
jsonrpc: "2.0".to_string(),
id: NumberOrString::Number(42),
method: "test/method".to_string(),
params: Some(json!({"foo": "bar"})),
};
let tower_req = lsp_req.clone().into_tower();
assert_eq!(tower_req.method(), "test/method");
assert_eq!(tower_req.id(), Some(&Id::Number(42)));
assert_eq!(tower_req.params(), Some(&json!({"foo": "bar"})));
let bridge_msg = BaseProtocolMessage::from_request(tower_req);
if let BaseProtocolMessage::Request(back) = bridge_msg {
assert_eq!(back.id, lsp_req.id);
assert_eq!(back.method, lsp_req.method);
assert_eq!(back.params, lsp_req.params);
} else {
panic!("Expected RequestMessage variant");
}
}
#[test]
fn test_notification_message_bridge() {
let lsp_notif = NotificationMessage {
jsonrpc: "2.0".to_string(),
method: "test/notify".to_string(),
params: Some(json!([1, 2, 3])),
};
let tower_req = lsp_notif.clone().into_tower();
assert_eq!(tower_req.method(), "test/notify");
assert_eq!(tower_req.id(), None);
assert_eq!(tower_req.params(), Some(&json!([1, 2, 3])));
let bridge_msg = BaseProtocolMessage::from_request(tower_req);
if let BaseProtocolMessage::Notification(back) = bridge_msg {
assert_eq!(back.method, lsp_notif.method);
assert_eq!(back.params, lsp_notif.params);
} else {
panic!("Expected NotificationMessage variant");
}
}
#[test]
fn test_response_message_bridge() {
let lsp_res = ResponseMessage::Success {
jsonrpc: "2.0".to_string(),
id: Some(NumberOrString::String("req-1".to_string())),
result: json!({"status": "ok"}),
};
let tower_res = lsp_res.clone().into_tower();
assert_eq!(tower_res.id(), &Id::String("req-1".to_string()));
assert_eq!(tower_res.result(), Some(&json!({"status": "ok"})));
let bridge_msg = BaseProtocolMessage::from_response(tower_res);
if let BaseProtocolMessage::Response(back) = bridge_msg {
assert_eq!(back, lsp_res);
} else {
panic!("Expected ResponseMessage variant");
}
}
#[test]
fn test_error_bridge() {
let lsp_err = ResponseError {
code: -32601,
message: "Method not found".to_string(),
data: None,
};
let tower_err: Error = lsp_err.clone().into_tower();
assert_eq!(tower_err.code, ErrorCode::MethodNotFound);
assert_eq!(tower_err.message, "Method not found");
let back_lsp_err = tower_err.into_lsp();
assert_eq!(back_lsp_err, lsp_err);
}
}