Skip to content

Instantly share code, notes, and snippets.

@hinzundcode
Last active February 18, 2020 15:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hinzundcode/59a224dd4ddb389ae7dd189b3ff95960 to your computer and use it in GitHub Desktop.
Save hinzundcode/59a224dd4ddb389ae7dd189b3ff95960 to your computer and use it in GitHub Desktop.
use std::collections::HashMap;
use futures::Future;
use futures::prelude::*;
use std::pin::Pin;
use futures_timer::Delay;
use std::time::Duration;
use serde::de::DeserializeOwned;
use serde_json::{json, Value};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "lowercase")]
enum RpcResponse {
Result(Value),
Error(Value),
}
async fn foo_handler(request: ()) -> Result<Value, String> {
Ok(json!({ "success": true }))
}
async fn bar_handler(request: ()) -> Result<Value, String> {
Err("fail".to_string())
}
async fn timer_handler(request: ()) -> Result<Value, String> {
Delay::new(Duration::from_secs(1)).await;
Ok(json!({ "success": true }))
}
struct Dispatcher {
handlers: HashMap<&'static str, Box<dyn Fn(Value) -> Pin<Box<dyn Future<Output = RpcResponse>>>>>,
}
impl Dispatcher {
fn add_handler<
Req: DeserializeOwned + Send,
Res: Serialize + Send,
Fut: 'static + Future<Output = Result<Res, String>>,
T: 'static + Fn(Req) -> Fut + Copy,
>(&mut self, name: &'static str, handler: T) {
self.handlers.insert(name, Box::new(move |req| Box::pin(async move {
let request: Req = serde_json::from_value(req).unwrap();
let response = handler(request).await;
match response {
Ok(x) => RpcResponse::Result(serde_json::to_value(x).unwrap()),
Err(x) => RpcResponse::Error(serde_json::to_value(x).unwrap()),
}
})));
}
async fn dispatch(&self, name: &'static str, request: Value) -> RpcResponse {
self.handlers[name](request).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[async_std::test]
async fn test_handler() {
let mut dispatcher = Dispatcher { handlers: Default::default() };
dispatcher.add_handler("foo", foo_handler);
dispatcher.add_handler("bar", bar_handler);
dispatcher.add_handler("timer", timer_handler);
let result = dispatcher.dispatch("foo", json!(null)).await;
assert_eq!(result, RpcResponse::Result(json!({ "success": true })));
}
#[async_std::test]
async fn test_async_wrapper() {
#[derive(Serialize, Deserialize, Debug)]
struct StorageGetRequest {
key: String,
}
#[derive(Serialize, Deserialize, Debug)]
struct StorageGetResponse {
value: String,
}
async fn storage_get(request: StorageGetRequest) -> Result<StorageGetResponse, String> {
if request.key == "foo" {
Ok(StorageGetResponse { value: "bar".to_string() })
} else {
Err("invalid key".to_string())
}
}
let mut dispatcher = Dispatcher { handlers: Default::default() };
dispatcher.add_handler("storage.get", storage_get);
let result = dispatcher.dispatch("storage.get", json!({ "key": "foo" })).await;
assert_eq!(result, RpcResponse::Result(json!({ "value": "bar" })));
let result = dispatcher.dispatch("storage.get", json!({ "key": "bla" })).await;
assert_eq!(result, RpcResponse::Error(json!("invalid key")));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment