use {
super::{
super::{
CallContext,
Function,
FunctionId,
Functions,
accept::CallReply,
derived_function_id,
effective_function_id,
},
Handler,
Stats,
registry::{DispatchError, Entry},
},
crate::{
discovery::PeerInfo,
primitives::{Bytes, Datum},
tickets::TicketValidator,
},
core::marker::PhantomData,
futures::FutureExt,
std::sync::Arc,
tokio::sync::Semaphore,
};
#[derive(Debug, thiserror::Error)]
pub enum BuilderError {
#[error("A handler for this function id is already registered")]
AlreadyExists,
}
pub struct HandlerConfig {
pub function_id: Option<FunctionId>,
pub require: Box<dyn Fn(&PeerInfo) -> bool + Send + Sync>,
pub caller_auth: Vec<Arc<dyn TicketValidator>>,
pub handler_auth: Vec<Arc<dyn TicketValidator>>,
pub max_concurrent: usize,
}
pub struct Builder<'f, Req: Datum, Res: Datum, E: Datum = ()> {
config: HandlerConfig,
functions: &'f Functions,
_marker: PhantomData<fn(&Req, &Res, &E)>,
}
impl<Req: Datum, Res: Datum, E: Datum> Builder<'_, Req, Res, E> {
#[must_use]
pub fn with_function_id(
mut self,
function_id: impl Into<FunctionId>,
) -> Self {
self.config.function_id = Some(function_id.into());
self
}
#[must_use]
pub fn require<F>(mut self, pred: F) -> Self
where
F: Fn(&PeerInfo) -> bool + Send + Sync + 'static,
{
let prev = self.config.require;
self.config.require = Box::new(move |peer| prev(peer) && pred(peer));
self
}
#[must_use]
pub fn require_ticket(self, validator: impl TicketValidator) -> Self {
self.require_caller_ticket(validator)
}
#[must_use]
pub fn require_caller_ticket(
mut self,
validator: impl TicketValidator,
) -> Self {
self.config.caller_auth.push(Arc::new(validator));
self
}
#[must_use]
pub fn require_handler_ticket(
mut self,
validator: impl TicketValidator,
) -> Self {
self.config.handler_auth.push(Arc::new(validator));
self
}
#[must_use]
pub const fn with_max_concurrent(mut self, max: usize) -> Self {
self.config.max_concurrent = max;
self
}
pub fn serve<F, Fut>(
mut self,
f: F,
) -> Result<Handler<Req, Res, E>, BuilderError>
where
F: Fn(Req, CallContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Res, E>> + Send + 'static,
{
if self.config.function_id.is_none() {
self.config.function_id = Some(derived_function_id::<Req, Res>());
}
self.serve_fn(FnAdapter {
f,
_marker: PhantomData,
})
}
pub fn serve_fn<F>(
self,
function: F,
) -> Result<Handler<Req, Res, E>, BuilderError>
where
F: Function<Req = Req, Res = Res, Err = E>,
{
let base = self.config.function_id.unwrap_or_else(F::signature);
let function_id = effective_function_id(
base,
&self.config.caller_auth,
&self.config.handler_auth,
);
let function = Arc::new(function);
let dispatch = Arc::new(move |payload: Bytes, ctx: CallContext| {
Req::decode(&payload).map_or_else(
|_| futures::future::ready(Err(DispatchError::DecodeRequest)).boxed(),
|req| {
let function = Arc::clone(&function);
async move {
match function.call(req, ctx).await {
Ok(res) => res
.encode()
.map(CallReply::Ok)
.map_err(|_| DispatchError::EncodeReply),
Err(err) => err
.encode()
.map(CallReply::Err)
.map_err(|_| DispatchError::EncodeReply),
}
}
.boxed()
},
)
});
let registry = Arc::clone(&self.functions.registry);
let cancel = self.functions.local.termination().child_token();
let stats = Arc::new(Stats::default());
let permits = Arc::new(Semaphore::new(
self.config.max_concurrent.min(Semaphore::MAX_PERMITS),
));
registry
.create(function_id, Entry {
dispatch,
require: self.config.require,
caller_auth: self.config.caller_auth,
permits,
cancel: cancel.clone(),
stats: Arc::clone(&stats),
})
.map_err(|()| BuilderError::AlreadyExists)?;
Ok(Handler::new(function_id, stats, registry, cancel))
}
}
impl<'f, Req: Datum, Res: Datum, E: Datum> Builder<'f, Req, Res, E> {
pub(in crate::functions) fn new(functions: &'f Functions) -> Self {
Self {
functions,
config: HandlerConfig {
function_id: None,
require: Box::new(|_| true),
caller_auth: Vec::new(),
handler_auth: Vec::new(),
max_concurrent: usize::MAX,
},
_marker: PhantomData,
}
}
}
struct FnAdapter<F, Req, Res, E> {
f: F,
_marker: PhantomData<fn(&Req, &Res, &E)>,
}
impl<F, Fut, Req, Res, E> Function for FnAdapter<F, Req, Res, E>
where
F: Fn(Req, CallContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Res, E>> + Send + 'static,
Req: Datum,
Res: Datum,
E: Datum,
{
type Err = E;
type Req = Req;
type Res = Res;
fn call(
&self,
req: Req,
ctx: CallContext,
) -> impl Future<Output = Result<Res, E>> + Send {
(self.f)(req, ctx)
}
}