use std::collections::HashMap;
use std::pin::Pin;
use std::sync::{Arc, RwLock};
use serde_json::Value;
use tokio_util::sync::CancellationToken;
use crate::jsonrpc::{JsonRpcError, RequestId};
type HandlerFuture = Pin<Box<dyn std::future::Future<Output = HandlerResult> + Send>>;
type HandlerFn = Arc<dyn Fn(Value, CancellationToken) -> HandlerFuture + Send + Sync>;
#[non_exhaustive]
#[derive(Debug)]
pub enum HandlerResult {
Ok(Value),
Err(JsonRpcError),
}
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum DispatchError {
#[error("method not found: {0}")]
UnknownMethod(String),
}
#[derive(Clone, Default)]
pub struct Dispatcher {
handlers: Arc<RwLock<HashMap<String, HandlerFn>>>,
cancellations: Arc<RwLock<HashMap<RequestId, CancellationToken>>>,
}
impl Dispatcher {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn register<F, Fut>(&self, method: impl Into<String>, handler: F)
where
F: Fn(Value, CancellationToken) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = HandlerResult> + Send + 'static,
{
let wrapped: HandlerFn = Arc::new(move |params, cancel| Box::pin(handler(params, cancel)));
if let Ok(mut guard) = self.handlers.write() {
guard.insert(method.into(), wrapped);
}
}
pub async fn dispatch(
&self,
id: &RequestId,
method: &str,
params: Value,
) -> Result<HandlerResult, DispatchError> {
let handler = self
.handlers
.read()
.ok()
.and_then(|guard| guard.get(method).cloned())
.ok_or_else(|| DispatchError::UnknownMethod(method.to_owned()))?;
let cancel = CancellationToken::new();
if let Ok(mut guard) = self.cancellations.write() {
guard.insert(id.clone(), cancel.clone());
}
let result = handler(params, cancel).await;
if let Ok(mut guard) = self.cancellations.write() {
guard.remove(id);
}
Ok(result)
}
pub fn cancel(&self, id: &RequestId) {
if let Ok(guard) = self.cancellations.read() {
if let Some(token) = guard.get(id) {
token.cancel();
}
}
}
}
impl std::fmt::Debug for Dispatcher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let handlers = self.handlers.read().map_or(0, |g| g.len());
let in_flight = self.cancellations.read().map_or(0, |g| g.len());
f.debug_struct("Dispatcher")
.field("handlers", &handlers)
.field("in_flight", &in_flight)
.finish()
}
}