summaryrefslogtreecommitdiff
path: root/support/texlab/crates/jsonrpc/src
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/crates/jsonrpc/src')
-rw-r--r--support/texlab/crates/jsonrpc/src/client.rs74
-rw-r--r--support/texlab/crates/jsonrpc/src/lib.rs72
-rw-r--r--support/texlab/crates/jsonrpc/src/server.rs137
-rw-r--r--support/texlab/crates/jsonrpc/src/types.rs144
4 files changed, 427 insertions, 0 deletions
diff --git a/support/texlab/crates/jsonrpc/src/client.rs b/support/texlab/crates/jsonrpc/src/client.rs
new file mode 100644
index 0000000000..2cf18af1ff
--- /dev/null
+++ b/support/texlab/crates/jsonrpc/src/client.rs
@@ -0,0 +1,74 @@
+use crate::types::*;
+use chashmap::CHashMap;
+use futures::channel::*;
+use futures::prelude::*;
+use futures_boxed::boxed;
+use serde::Serialize;
+use serde_json::json;
+use std::sync::atomic::{AtomicU64, Ordering};
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+pub trait ResponseHandler {
+ #[boxed]
+ async fn handle(&self, response: Response);
+}
+
+pub struct Client {
+ output: mpsc::Sender<String>,
+ request_id: AtomicU64,
+ senders_by_id: CHashMap<Id, oneshot::Sender<Result<serde_json::Value>>>,
+}
+
+impl Client {
+ pub fn new(output: mpsc::Sender<String>) -> Self {
+ Client {
+ output,
+ request_id: AtomicU64::new(0),
+ senders_by_id: CHashMap::new(),
+ }
+ }
+
+ pub async fn send_request<T: Serialize>(
+ &self,
+ method: String,
+ params: T,
+ ) -> Result<serde_json::Value> {
+ let id = self.request_id.fetch_add(1, Ordering::SeqCst);
+ let request = Request::new(method, json!(params), Id::Number(id));
+
+ let (result_tx, result_rx) = oneshot::channel();
+ self.senders_by_id.insert(request.id.clone(), result_tx);
+ self.send(Message::Request(request)).await;
+
+ result_rx.await.unwrap()
+ }
+
+ pub async fn send_notification<T: Serialize>(&self, method: String, params: T) {
+ let notification = Notification::new(method, json!(params));
+ self.send(Message::Notification(notification)).await;
+ }
+
+ async fn send(&self, message: Message) {
+ let mut output = self.output.clone();
+ let json = serde_json::to_string(&message).unwrap();
+ output.send(json).await.unwrap();
+ }
+}
+
+impl ResponseHandler for Client {
+ #[boxed]
+ async fn handle(&self, response: Response) {
+ let id = response.id.expect("Expected response with id");
+ let result = match response.error {
+ Some(why) => Err(why),
+ None => Ok(response.result.unwrap_or(serde_json::Value::Null)),
+ };
+
+ let result_tx = self
+ .senders_by_id
+ .remove(&id)
+ .expect("Unexpected response received");
+ result_tx.send(result).unwrap();
+ }
+}
diff --git a/support/texlab/crates/jsonrpc/src/lib.rs b/support/texlab/crates/jsonrpc/src/lib.rs
new file mode 100644
index 0000000000..8786598d58
--- /dev/null
+++ b/support/texlab/crates/jsonrpc/src/lib.rs
@@ -0,0 +1,72 @@
+pub mod client;
+pub mod server;
+mod types;
+
+pub use self::{
+ client::{Client, ResponseHandler},
+ server::{handle_notification, handle_request, Middleware, RequestHandler},
+ types::*,
+};
+
+use futures::channel::*;
+use futures::prelude::*;
+use log::error;
+use std::io;
+use std::sync::Arc;
+
+pub struct MessageHandler<S, C, I> {
+ pub server: Arc<S>,
+ pub client: Arc<C>,
+ pub input: I,
+ pub output: mpsc::Sender<String>,
+}
+
+impl<S, C, I> MessageHandler<S, C, I>
+where
+ S: RequestHandler + Middleware + Send + Sync + 'static,
+ C: ResponseHandler + Send + Sync + 'static,
+ I: Stream<Item = io::Result<String>> + Unpin,
+{
+ pub async fn listen(&mut self) {
+ while let Some(json) = self.input.next().await {
+ self.server.before_message().await;
+
+ match serde_json::from_str(&json.unwrap()).map_err(|_| Error::parse_error()) {
+ Ok(Message::Request(request)) => {
+ let server = Arc::clone(&self.server);
+ let mut output = self.output.clone();
+ tokio::spawn(async move {
+ let response = server.handle_request(request).await;
+ if let Some(error) = response.error.as_ref() {
+ error!("{:?}", error);
+ }
+ let json = serde_json::to_string(&response).unwrap();
+ output.send(json).await.unwrap();
+ server.after_message().await;
+ });
+ }
+ Ok(Message::Notification(notification)) => {
+ self.server.handle_notification(notification);
+ self.after_message();
+ }
+ Ok(Message::Response(response)) => {
+ self.client.handle(response).await;
+ self.after_message();
+ }
+ Err(why) => {
+ let response = Response::error(why, None);
+ let json = serde_json::to_string(&response).unwrap();
+ self.output.send(json).await.unwrap();
+ self.after_message();
+ }
+ };
+ }
+ }
+
+ fn after_message(&self) {
+ let server = Arc::clone(&self.server);
+ tokio::spawn(async move {
+ server.after_message().await;
+ });
+ }
+}
diff --git a/support/texlab/crates/jsonrpc/src/server.rs b/support/texlab/crates/jsonrpc/src/server.rs
new file mode 100644
index 0000000000..ccc04f286e
--- /dev/null
+++ b/support/texlab/crates/jsonrpc/src/server.rs
@@ -0,0 +1,137 @@
+use crate::types::*;
+use futures::prelude::*;
+use futures_boxed::boxed;
+use serde::de::DeserializeOwned;
+use serde::Serialize;
+use serde_json::json;
+
+pub type Result<T> = std::result::Result<T, String>;
+
+pub trait RequestHandler {
+ #[boxed]
+ async fn handle_request(&self, request: Request) -> Response;
+
+ fn handle_notification(&self, notification: Notification);
+}
+
+pub trait Middleware {
+ #[boxed]
+ async fn before_message(&self);
+
+ #[boxed]
+ async fn after_message(&self);
+}
+
+pub async fn handle_request<'a, H, F, I, O>(request: Request, handler: H) -> Response
+where
+ H: Fn(I) -> F + Send + Sync + 'a,
+ F: Future<Output = Result<O>> + Send,
+ I: DeserializeOwned + Send,
+ O: Serialize,
+{
+ let handle = |json| {
+ async move {
+ let params: I = serde_json::from_value(json).map_err(|_| Error::deserialize_error())?;
+ let result = handler(params).await.map_err(Error::internal_error)?;
+ Ok(result)
+ }
+ };
+
+ match handle(request.params).await {
+ Ok(result) => Response::result(json!(result), request.id),
+ Err(error) => Response::error(error, Some(request.id)),
+ }
+}
+
+pub fn handle_notification<'a, H, I>(notification: Notification, handler: H)
+where
+ H: Fn(I) -> () + Send + Sync + 'a,
+ I: DeserializeOwned + Send,
+{
+ let params =
+ serde_json::from_value(notification.params).expect(&Error::deserialize_error().message);
+ handler(params);
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use futures::executor::block_on;
+
+ const METHOD_NAME: &str = "foo";
+
+ async fn increment(i: i32) -> Result<i32> {
+ Ok(i + 1)
+ }
+
+ fn panic(_params: ()) {
+ panic!("success");
+ }
+
+ fn setup_request<T: Serialize>(value: T) -> Request {
+ Request {
+ jsonrpc: PROTOCOL_VERSION.to_owned(),
+ params: json!(value),
+ method: METHOD_NAME.to_owned(),
+ id: Id::Number(0),
+ }
+ }
+
+ fn setup_notification() -> Notification {
+ Notification {
+ jsonrpc: PROTOCOL_VERSION.to_owned(),
+ method: METHOD_NAME.to_owned(),
+ params: json!(()),
+ }
+ }
+
+ #[test]
+ fn test_request_valid() {
+ let value = 42;
+ let request = setup_request(value);
+
+ let response = block_on(handle_request(request.clone(), increment));
+ let expected = Response {
+ jsonrpc: request.jsonrpc,
+ result: Some(json!(block_on(increment(value)).unwrap())),
+ error: None,
+ id: Some(request.id),
+ };
+
+ assert_eq!(response, expected);
+ }
+
+ #[test]
+ fn test_request_invalid_params() {
+ let request = setup_request((0, 0));
+
+ let response = block_on(handle_request(request.clone(), increment));
+ let expected = Response {
+ jsonrpc: request.jsonrpc.clone(),
+ result: None,
+ error: Some(Error::deserialize_error()),
+ id: Some(request.id),
+ };
+
+ assert_eq!(response, expected);
+ }
+
+ #[test]
+ #[should_panic(expected = "success")]
+ fn test_notification_valid() {
+ let notification = setup_notification();
+ handle_notification(notification, panic);
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_notification_invalid_params() {
+ let notification = setup_notification();
+ let notification = Notification {
+ params: json!(0),
+ ..notification
+ };
+
+ handle_notification(notification, panic);
+ }
+}
diff --git a/support/texlab/crates/jsonrpc/src/types.rs b/support/texlab/crates/jsonrpc/src/types.rs
new file mode 100644
index 0000000000..90116ac793
--- /dev/null
+++ b/support/texlab/crates/jsonrpc/src/types.rs
@@ -0,0 +1,144 @@
+use serde::{Deserialize, Serialize};
+use serde_repr::*;
+
+pub const PROTOCOL_VERSION: &str = "2.0";
+
+#[derive(Debug, Eq, Hash, PartialEq, Clone, Deserialize, Serialize)]
+#[serde(untagged)]
+pub enum Id {
+ Number(u64),
+ String(String),
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize_repr, Serialize_repr)]
+#[repr(i32)]
+pub enum ErrorCode {
+ ParseError = -32700,
+ InvalidRequest = -32600,
+ MethodNotFound = -32601,
+ InvalidParams = -32602,
+ InternalError = -32603,
+ ServerNotInitialized = -32002,
+ UnknownErrorCode = -32001,
+ RequestCancelled = -32800,
+}
+
+#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
+pub struct Error {
+ pub code: ErrorCode,
+ pub message: String,
+
+ #[serde(skip_serializing_if = "serde_json::Value::is_null")]
+ pub data: serde_json::Value,
+}
+
+impl Error {
+ pub fn parse_error() -> Self {
+ Self {
+ code: ErrorCode::ParseError,
+ message: "Could not parse the input".to_owned(),
+ data: serde_json::Value::Null,
+ }
+ }
+
+ pub fn method_not_found_error() -> Self {
+ Self {
+ code: ErrorCode::MethodNotFound,
+ message: "Method not found".to_owned(),
+ data: serde_json::Value::Null,
+ }
+ }
+
+ pub fn deserialize_error() -> Self {
+ Self {
+ code: ErrorCode::InvalidParams,
+ message: "Could not deserialize parameter object".to_owned(),
+ data: serde_json::Value::Null,
+ }
+ }
+
+ pub fn internal_error(message: String) -> Self {
+ Self {
+ code: ErrorCode::InternalError,
+ message,
+ data: serde_json::Value::Null,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
+pub struct Request {
+ pub jsonrpc: String,
+ pub method: String,
+ pub params: serde_json::Value,
+ pub id: Id,
+}
+
+impl Request {
+ pub fn new(method: String, params: serde_json::Value, id: Id) -> Self {
+ Request {
+ jsonrpc: PROTOCOL_VERSION.to_owned(),
+ method,
+ params,
+ id,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
+pub struct Response {
+ pub jsonrpc: String,
+
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub result: Option<serde_json::Value>,
+
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub error: Option<Error>,
+
+ pub id: Option<Id>,
+}
+
+impl Response {
+ pub fn result(result: serde_json::Value, id: Id) -> Self {
+ Response {
+ jsonrpc: PROTOCOL_VERSION.to_owned(),
+ result: Some(result),
+ error: None,
+ id: Some(id),
+ }
+ }
+
+ pub fn error(error: Error, id: Option<Id>) -> Self {
+ Response {
+ jsonrpc: PROTOCOL_VERSION.to_owned(),
+ result: None,
+ error: Some(error),
+ id,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
+pub struct Notification {
+ pub jsonrpc: String,
+ pub method: String,
+ pub params: serde_json::Value,
+}
+
+impl Notification {
+ pub fn new(method: String, params: serde_json::Value) -> Self {
+ Notification {
+ jsonrpc: PROTOCOL_VERSION.to_owned(),
+ method,
+ params,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
+#[serde(untagged)]
+pub enum Message {
+ Request(Request),
+ Notification(Notification),
+ Response(Response),
+}