uxar 0.1.3

Opinionated Rust web framework built on Axum for Postgres-backed JSON APIs
Documentation
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,
};

/// Extracts typed arguments from context, excluding payload extractors.
///
/// Types implementing this can appear at any position in a handler signature.
/// Payload extractors implement only `FromContext`.
pub trait FromContextParts<C>: Sized + Send {
    fn from_context_parts(ctx: &C) -> Result<Self, CallError>;
}

/// Extracts typed arguments from context for the last argument position.
///
/// Payload extractors (like `Payload<T>`) implement only this trait.
/// Other extractors implement both this and `FromContextParts`.
///
/// This enforces payload-must-be-last: handlers can only extract payloads
/// as the final argument.
pub trait FromContext<C>: Sized + Send {
    fn from_context(ctx: C) -> Result<Self, CallError>;

    fn deserializer() -> Option<fn(&str) -> Result<PayloadData, CallError>> {
        None
    }
}

/// Provides type-erased payload data from context.
/// Implement for context types that carry payloads (SignalContext, TaskContext).
pub trait IntoPayloadData {
    fn into_payload_data(self) -> PayloadData;
}


/// Converts handler output into `Result<PayloadData, E>`.
///
/// Supported return types: `Payload<T>`, `()`, `Result<Payload<T>, E>`, `Result<(), E>`.
/// For truly infallible handlers, use `std::convert::Infallible` as error type.
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(()))
    }
}

// Unit type represents handlers with no arguments.
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);

/// Type-erased payload container wrapping `Arc<dyn Any>`.
/// Enables runtime downcasting and uniform storage of heterogeneous payloads.
#[derive(Debug, Clone)]
pub struct PayloadData {
    type_id: TypeId,
    inner: Arc<dyn std::any::Any + Send + Sync>,
}

impl PayloadData {
    
    /// Wraps existing `Arc<T>` in type-erased container.
    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
    }

    /// Returns the runtime `TypeId` of the wrapped value.
    #[inline]
    pub fn payload_type_id(&self) -> TypeId {
        self.type_id
    }

    /// Downcasts to `&T`, returning `None` on type mismatch.
    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()
    }

}

/// Type-erased async handler storing context and error types only.
///
/// Stores handlers with different payload/output types uniformly via `PayloadData`.
/// Generic over context `C` and error `E`; input/output types erased at runtime.
///
/// Payload-must-be-last constraint enforced via `FromContext`/`FromContextParts` split.
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>>,
    // serializer: Option<fn(PayloadData) -> Result<String, 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,
            // serializer: self.serializer,
            inner: Arc::clone(&self.inner),
        }
    }
}

impl<C, E> Callable<C, E>
where
    C: Send + 'static,
    E: Send + 'static,
{
    /// Returns handler metadata for introspection and API documentation.
    #[inline]
    pub fn inspect(&self) -> &CallSpec {
        &self.spec
    }

    /// Deserializes JSON string into type-erased payload.
    /// Returns error if no deserializer registered or parsing fails.
    #[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,
{
    /// Wraps typed handler into type-erased `Callable`.
    ///
    /// Captures spec, optional JSON deserializer, and type-erases input/output.
    /// Payload-must-be-last enforced via `FromContext` bound on `Args`.
    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 serializer = |p: PayloadData|{
        //     let value = p.downcast_ref::<O>().ok_or(CallError::SerializeFailed)?;
        //     serde_json::to_string(value).map_err(|_| CallError::SerializeFailed)
        // };

        let deserializer = Args::deserializer();

        Callable {
            spec,
            type_id,
            deserializer,
            // serializer: Some(serializer),
            inner,
        }
    }

    /// Invokes handler with context, returning type-erased output.
    #[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)
        }
    }

    // pub fn serialize_output<T: serde::Serialize>(&self, data: PayloadData) -> Result<String, CallError> {
    //     if let Some(ser) = self.serializer {
    //         ser(data)
    //     } else {
    //         Err(CallError::SerializeFailed)
    //     }

    // }
}