use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc};
use futures::FutureExt;
use serde::Serialize;
use serde_json::Value;
use tracing::{debug, error};
use crate::{
error::{Error, ReservedErrorCode},
request::{Params, Request},
response::Response,
};
type HandleRequestFuture = Pin<Box<dyn Future<Output = Result<Value, Error>> + Send>>;
type RequestHandler = Arc<dyn Fn(Option<Params>) -> HandleRequestFuture + Send + Sync>;
#[derive(Clone)]
pub struct RequestHandlers(Arc<HashMap<&'static str, RequestHandler>>);
impl RequestHandlers {
pub(crate) async fn handle_request(&self, request: Request) -> Response {
let handler = match self.0.get(request.method.as_str()) {
Some(handler) => Arc::clone(handler),
None => {
debug!(requested_method = %request.method.as_str(), "failed to get handler");
let error = Error::new(
ReservedErrorCode::MethodNotFound,
format!(
"'{}' is not a supported json-rpc method on this server",
request.method.as_str()
),
);
return Response::new_failure(request.id, error);
}
};
match handler(request.params).await {
Ok(result) => Response::new_success(request.id, result),
Err(error) => Response::new_failure(request.id, error),
}
}
}
#[derive(Default)]
pub struct RequestHandlersBuilder(HashMap<&'static str, RequestHandler>);
impl RequestHandlersBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn register_handler<Func, Fut, T>(&mut self, method: &'static str, handler: Arc<Func>)
where
Func: Fn(Option<Params>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<T, Error>> + Send,
T: Serialize + 'static,
{
let handler = Arc::clone(&handler);
let wrapped_handler = move |maybe_params| {
let handler = Arc::clone(&handler);
async move {
let success = Arc::clone(&handler)(maybe_params).await?;
serde_json::to_value(success).map_err(|error| {
error!(%error, "failed to encode json-rpc response value");
Error::new(
ReservedErrorCode::InternalError,
format!("failed to encode json-rpc response value: {}", error),
)
})
}
.boxed()
};
if self.0.insert(method, Arc::new(wrapped_handler)).is_some() {
error!(
method,
"already registered a handler for this json-rpc request method"
);
}
}
pub fn build(self) -> RequestHandlers {
RequestHandlers(Arc::new(self.0))
}
}