summaryrefslogtreecommitdiff
path: root/support/texlab/crates/jsonrpc/src/server.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/crates/jsonrpc/src/server.rs')
-rw-r--r--support/texlab/crates/jsonrpc/src/server.rs137
1 files changed, 137 insertions, 0 deletions
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);
+ }
+}