use std::collections::HashMap;
use serde_json::Value;
use crate::document::TrustTask;
use crate::error::RejectReason;
use crate::payload::Payload;
type BoxedHandler<R> = Box<dyn Fn(TrustTask<Value>) -> Result<R, RejectReason> + Send + Sync>;
pub struct Dispatcher<R> {
handlers: HashMap<String, BoxedHandler<R>>,
}
impl<R> Default for Dispatcher<R> {
fn default() -> Self {
Self::new()
}
}
impl<R> Dispatcher<R> {
pub fn new() -> Self {
Self {
handlers: HashMap::new(),
}
}
pub fn on<P, F>(mut self, handler: F) -> Self
where
P: Payload + 'static,
F: Fn(TrustTask<P>) -> R + Send + Sync + 'static,
{
let wrapped = move |doc: TrustTask<Value>| -> Result<R, RejectReason> {
let typed = downcast_payload::<P>(doc)?;
Ok(handler(typed))
};
self.handlers
.insert(canonical_key(&P::type_uri()), Box::new(wrapped));
self
}
pub fn dispatch(&self, doc: TrustTask<Value>) -> Result<R, RejectReason> {
let key = canonical_key(&doc.type_uri);
match self.handlers.get(&key) {
Some(handler) => handler(doc),
None => Err(RejectReason::UnsupportedType { type_uri: key }),
}
}
pub fn registered_uris(&self) -> Vec<&str> {
let mut v: Vec<&str> = self.handlers.keys().map(String::as_str).collect();
v.sort_unstable();
v
}
}
fn canonical_key(uri: &crate::type_uri::TypeUri) -> String {
uri.for_routing().to_string()
}
fn downcast_payload<P>(doc: TrustTask<Value>) -> Result<TrustTask<P>, RejectReason>
where
P: Payload,
{
let TrustTask {
id,
thread_id,
type_uri,
issuer,
recipient,
issued_at,
expires_at,
payload,
context,
proof,
extra,
} = doc;
let payload: P =
serde_json::from_value(payload).map_err(|e| RejectReason::MalformedRequest {
reason: format!("payload does not match {}: {e}", P::TYPE_URI),
})?;
Ok(TrustTask {
id,
thread_id,
type_uri,
issuer,
recipient,
issued_at,
expires_at,
payload,
context,
proof,
extra,
})
}