use std::{any::TypeId, ops::DerefMut};
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use super::specs::{
CallError, CallSpec, IntoArgSpecs, IntoReturnPart, Specable, Tuple1, Tuple2, Tuple3, Tuple4,
Tuple5, Tuple6,
};
pub trait FromContextParts<C>: Sized + Send {
fn from_context_parts(ctx: &C) -> Result<Self, CallError>;
}
pub trait FromContext<C>: Sized + Send {
fn from_context(ctx: C) -> Result<Self, CallError>;
fn deserializer() -> Option<fn(&str) -> Result<PayloadData, CallError>> {
None
}
}
pub trait IntoPayloadData {
fn into_payload_data(self) -> PayloadData;
}
pub trait IntoOutput<E> {
fn into_output(self) -> Result<PayloadData, E>;
}
impl<T, E> IntoOutput<E> for Result<T, E>
where
T: Send + Sync + 'static,
{
fn into_output(self) -> Result<PayloadData, E> {
self.map(PayloadData::new)
}
}
impl<E> IntoOutput<E> for () {
fn into_output(self) -> Result<PayloadData, E> {
Ok(PayloadData::new(()))
}
}
impl<C> FromContextParts<C> for () {
fn from_context_parts(_ctx: &C) -> Result<Self, CallError> {
Ok(())
}
}
impl<C> FromContext<C> for () {
fn from_context(_ctx: C) -> Result<Self, CallError> {
Ok(())
}
}
macro_rules! impl_context {
([$($ty:ident),*], $last:ident, $tuple:ident) => {
#[allow(non_snake_case, unused_mut, unused_variables)]
impl<C, $($ty,)* $last> FromContextParts<C> for $tuple<$($ty,)* $last>
where
$($ty: FromContextParts<C>,)*
$last: FromContextParts<C>,
{
fn from_context_parts(ctx: &C) -> Result<Self, CallError> {
$(let $ty = $ty::from_context_parts(ctx)?;)*
let $last = $last::from_context_parts(ctx)?;
Ok($tuple($($ty,)* $last))
}
}
#[allow(non_snake_case, unused_mut, unused_variables)]
impl<C, $($ty,)* $last> FromContext<C> for $tuple<$($ty,)* $last>
where
$($ty: FromContextParts<C>,)*
$last: FromContext<C>,
{
fn from_context(ctx: C) -> Result<Self, CallError> {
$(let $ty = $ty::from_context_parts(&ctx)?;)*
let $last = $last::from_context(ctx)?;
Ok($tuple($($ty,)* $last))
}
fn deserializer() -> Option<fn(&str) -> Result<PayloadData, CallError>> {
$last::deserializer()
}
}
};
}
impl_context!([], T1, Tuple1);
impl_context!([T1], T2, Tuple2);
impl_context!([T1, T2], T3, Tuple3);
impl_context!([T1, T2, T3], T4, Tuple4);
impl_context!([T1, T2, T3, T4], T5, Tuple5);
impl_context!([T1, T2, T3, T4, T5], T6, Tuple6);
#[derive(Debug, Clone)]
pub struct PayloadData {
type_id: TypeId,
inner: Arc<dyn std::any::Any + Send + Sync>,
}
impl PayloadData {
pub fn from_arc<T: Send + Sync + 'static>(inner: Arc<T>) -> Self {
Self {
inner,
type_id: TypeId::of::<T>(),
}
}
pub fn new<T: Send + Sync + 'static>(value: T) -> Self {
Self {
inner: Arc::new(value),
type_id: TypeId::of::<T>(),
}
}
pub(crate) fn into_any_arc(self) -> Arc<dyn std::any::Any + Send + Sync> {
self.inner
}
#[inline]
pub fn payload_type_id(&self) -> TypeId {
self.type_id
}
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.inner.downcast_ref::<T>()
}
pub fn downcast_arc<T: 'static + Send + Sync >(self) -> Option<Arc<T>> {
self.inner
.downcast::<T>()
.ok()
}
}
pub struct Callable<C, E = CallError>
where
C: Send + 'static,
E: Send + 'static,
{
spec: Arc<CallSpec>,
pub(crate) type_id: TypeId,
deserializer: Option<fn(&str) -> Result<PayloadData, CallError>>,
inner: Arc<
dyn Fn(C) -> Pin<Box<dyn Future<Output = Result<PayloadData, E>> + Send>> + Send + Sync,
>,
}
impl<C, E> Clone for Callable<C, E>
where
C: Send + 'static,
C: Send + 'static,
E: Send + 'static,
{
fn clone(&self) -> Self {
Self {
spec: Arc::clone(&self.spec),
type_id: self.type_id,
deserializer: self.deserializer,
inner: Arc::clone(&self.inner),
}
}
}
impl<C, E> Callable<C, E>
where
C: Send + 'static,
E: Send + 'static,
{
#[inline]
pub fn inspect(&self) -> &CallSpec {
&self.spec
}
#[inline]
pub fn deserialize(&self, data: &str) -> Result<PayloadData, CallError> {
if let Some(deser) = self.deserializer {
deser(data)
} else {
Err(CallError::TypeMismatch)
}
}
}
impl<C, E> Callable<C, E>
where
C: Send + 'static,
E: From<CallError> + Send + 'static,
{
pub fn new<H, O, Args>(handler: H) -> Self
where
O: IntoOutput<E> + IntoReturnPart + Send + 'static,
H: Specable<Args, Output = O> + Send + Sync + 'static,
Args: FromContext<C> + IntoArgSpecs,
{
let spec = Arc::new(CallSpec::new(&handler));
let type_id = spec.payload_type().unwrap_or(TypeId::of::<()>());
let handler = Arc::new(handler);
let inner = Arc::new(
move |ctx: C| -> Pin<Box<dyn Future<Output = Result<PayloadData, E>> + Send>> {
let handler = Arc::clone(&handler);
Box::pin(async move {
let args = Args::from_context(ctx).map_err(E::from)?;
let result = handler.call(args).await;
result.into_output()
})
},
);
let deserializer = Args::deserializer();
Callable {
spec,
type_id,
deserializer,
inner,
}
}
#[inline]
pub fn call(&self, ctx: C) -> Pin<Box<dyn Future<Output = Result<PayloadData, E>> + Send>> {
(self.inner)(ctx)
}
pub fn deserialize_input(&self, data: &str) -> Result<PayloadData, CallError> {
if let Some(deser) = self.deserializer {
deser(data)
} else {
Err(CallError::DeserializeFailed)
}
}
}