use {
crate::{
discovery::{Discovery, PeerEntry},
network::{
self,
LocalNode,
ProtocolProvider,
link::{self, Protocol},
},
primitives::{Datum, Digest, UniqueId},
tickets::TicketValidator,
},
accept::Acceptor,
handler::Registry,
iroh::protocol::RouterBuilder,
std::sync::Arc,
};
mod accept;
pub mod caller;
mod config;
pub mod handler;
pub mod status;
pub use {
caller::{CallError, Caller},
config::{Config, ConfigBuilder, ConfigBuilderError},
handler::Handler,
};
pub type FunctionId = UniqueId;
pub trait Function: Send + Sync + 'static {
type Req: Datum;
type Res: Datum;
type Err: Datum;
fn call(
&self,
req: Self::Req,
ctx: CallContext,
) -> impl Future<Output = Result<Self::Res, Self::Err>> + Send;
fn signature() -> UniqueId {
Digest::from_parts(&[
core::any::type_name::<Self>(),
core::any::type_name::<Self::Req>(),
core::any::type_name::<Self::Res>(),
])
}
}
pub trait FunctionHandler {
type Req: Datum;
type Res: Datum;
type Err: Datum;
type Handler;
fn handler<F, Fut>(
network: &crate::Network,
serve: F,
) -> Result<Self::Handler, handler::BuilderError>
where
F: Fn(Self::Req, CallContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Self::Res, Self::Err>> + Send + 'static;
}
pub trait FunctionCaller {
type Caller;
fn caller(network: &crate::Network) -> Self::Caller;
fn online_caller(
network: &crate::Network,
) -> impl Future<Output = Self::Caller> + Send + Sync + 'static;
}
pub type HandlerOf<F> = <F as FunctionHandler>::Handler;
pub type CallerOf<F> = <F as FunctionCaller>::Caller;
#[macro_export]
macro_rules! function {
(#[$($meta:tt)*] $($rest:tt)*) => {
$crate::function! { @attrs [#[$($meta)*]] $($rest)* }
};
(@attrs [$($attrs:tt)*] #[$($meta:tt)*] $($rest:tt)*) => {
$crate::function! { @attrs [$($attrs)* #[$($meta)*]] $($rest)* }
};
(@attrs [$($attrs:tt)*] $($rest:tt)*) => {
$crate::__function_impl! { @$crate; $($attrs)* $($rest)* }
};
($($tt:tt)*) => {
$crate::__function_impl! { @$crate; $($tt)* }
};
}
#[derive(Debug, Clone)]
pub struct CallContext {
caller: PeerEntry,
}
impl CallContext {
pub(crate) const fn new(caller: PeerEntry) -> Self {
Self { caller }
}
pub const fn caller(&self) -> &PeerEntry {
&self.caller
}
pub fn into_caller(self) -> PeerEntry {
self.caller
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct FunctionDef<Req: Datum, Res: Datum, E: Datum = ()> {
pub function_id: Option<FunctionId>,
_marker: core::marker::PhantomData<fn(&Req, &Res, &E)>,
}
impl<Req: Datum, Res: Datum, E: Datum> Clone for FunctionDef<Req, Res, E> {
fn clone(&self) -> Self {
*self
}
}
impl<Req: Datum, Res: Datum, E: Datum> Copy for FunctionDef<Req, Res, E> {}
impl<Req: Datum, Res: Datum, E: Datum> Default for FunctionDef<Req, Res, E> {
fn default() -> Self {
Self::new()
}
}
impl<Req: Datum, Res: Datum, E: Datum> FunctionDef<Req, Res, E> {
pub const fn new() -> Self {
Self {
function_id: None,
_marker: core::marker::PhantomData,
}
}
#[must_use]
pub const fn with_function_id(function_id: FunctionId) -> Self {
Self {
function_id: Some(function_id),
_marker: core::marker::PhantomData,
}
}
}
pub(crate) fn derived_function_id<Req: Datum, Res: Datum>() -> FunctionId {
Digest::from_parts(&[
core::any::type_name::<Req>(),
core::any::type_name::<Res>(),
])
}
pub(crate) fn effective_function_id(
base: FunctionId,
caller_auth: &[Arc<dyn TicketValidator>],
handler_auth: &[Arc<dyn TicketValidator>],
) -> FunctionId {
let mut id = base;
for validator in caller_auth {
id = id.derive(validator.signature());
}
for validator in handler_auth {
id = id.derive(validator.signature());
}
id
}
pub struct Functions {
config: Arc<Config>,
local: LocalNode,
discovery: Discovery,
registry: Arc<Registry>,
}
impl Functions {
pub fn handler<Req: Datum, Res: Datum, E: Datum>(
&self,
) -> handler::Builder<'_, Req, Res, E> {
handler::Builder::new(self)
}
#[allow(clippy::needless_pass_by_value)]
pub fn handler_of<Req: Datum, Res: Datum, E: Datum>(
&self,
def: FunctionDef<Req, Res, E>,
) -> handler::Builder<'_, Req, Res, E> {
let mut builder = self.handler::<Req, Res, E>();
if let Some(function_id) = def.function_id {
builder = builder.with_function_id(function_id);
}
builder
}
pub fn caller<Req: Datum, Res: Datum, E: Datum>(
&self,
) -> caller::Builder<'_, Req, Res, E> {
caller::Builder::new(self)
}
#[allow(clippy::needless_pass_by_value)]
pub fn caller_of<Req: Datum, Res: Datum, E: Datum>(
&self,
def: FunctionDef<Req, Res, E>,
) -> caller::Builder<'_, Req, Res, E> {
let mut builder = self.caller::<Req, Res, E>();
if let Some(function_id) = def.function_id {
builder = builder.with_function_id(function_id);
}
builder
}
pub fn caller_for<F: Function>(
&self,
) -> caller::Builder<'_, F::Req, F::Res, F::Err> {
self
.caller::<F::Req, F::Res, F::Err>()
.with_function_id(F::signature())
}
}
impl Functions {
pub(crate) fn new(
local: LocalNode,
discovery: &Discovery,
config: Config,
) -> Self {
Self {
local: local.clone(),
config: Arc::new(config),
discovery: discovery.clone(),
registry: Arc::new(Registry::new(local, discovery.clone())),
}
}
}
impl ProtocolProvider for Functions {
fn install(&self, protocols: RouterBuilder) -> RouterBuilder {
protocols.accept(Self::ALPN, Acceptor::new(self))
}
}
impl link::Protocol for Functions {
const ALPN: &'static [u8] = b"/mosaik/functions/1.0";
}
network::error::make_close_reason!(
struct FunctionNotFound, 11_404);
network::error::make_close_reason!(
struct NotAllowed, 11_403);
network::error::make_close_reason!(
struct NoCapacity, 11_509);
network::error::make_close_reason!(
struct HandlerFailure, 11_500);