occams-rpc 0.3.0

razor-rpc is a modular, pluggable RPC for high throughput scenario, supports various runtimes, with a low-level streaming interface, and high-level remote API call interface.
Documentation
use super::task::APIServerReq;
use crate::{Codec, error::RpcIntErr};
use rustc_hash::FxHashMap;
use std::sync::Arc;

/// Service handling code generated by macro
///
/// A ServiceStatic can be acquired by:
/// - `Arc<T>` where `T: ServiceStatic`,
/// - attr macro [service](crate::server::service): a class with multi method, if only one service, ignore [APIServerReq::service].
/// - attr macro [service_mux_struct](crate::server::service_mux_struct): a struct that contains several service, match [APIServerReq::service] by [ServiceStatic::SERVICE_NAME] field,
/// - [ServiceMuxDyn]: match multiple `dyn ServiceDyn` with SERVICE_NAME  (ServiceStatic auto impl [ServiceDyn])
pub trait ServiceStatic<C: Codec>: Send + Sync + 'static + Sized {
    /// For server_mux_struct to match
    const SERVICE_NAME: &'static str;

    /// the [`#[service]`](crate::server::service) macro should generate code like this:
    /// ```no_compile
    /// match req.method
    ///     match req.decode::<RequestType>() {
    ///         Err(())=>{
    ///             req.set_rpc_error(razor_rpc_core::error::RpcIntErr::Decode);
    ///             returnl
    ///         }
    ///         Ok(arg)=>{
    ///             match self.#method(arg).await {
    ///                 Ok(resp)=>{
    ///                     req.set_result(resp);
    ///                 }
    ///                 Err(e)=>{
    ///                     req.set_error::<E: RpcErrCodec>(e)
    ///                 }
    ///             }
    ///         }
    ///     }
    /// ```
    fn serve(&self, req: APIServerReq<C>) -> impl Future<Output = ()> + Send + Sized;
}

impl<S: ServiceStatic<C>, C: Codec> ServiceStatic<C> for Arc<S> {
    const SERVICE_NAME: &'static str = S::SERVICE_NAME;

    #[inline(always)]
    fn serve(&self, req: APIServerReq<C>) -> impl Future<Output = ()> + Send + Sized {
        self.as_ref().serve(req)
    }
}

/// Service trait for dyn dispatch, used by [ServiceMuxDyn]
///
/// NOTE:
/// - [ServiceStatic] auto impl `ServiceDyn`
/// - It cannot be used by [service_mux_struct](crate::server::service_mux_struct)
#[async_trait::async_trait]
pub trait ServiceDyn<C: Codec>: Send + Sync + 'static {
    fn get_service_name(&self) -> &'static str;

    async fn serve_dyn(&self, req: APIServerReq<C>);
}

#[async_trait::async_trait]
impl<S: ServiceStatic<C>, C: Codec> ServiceDyn<C> for S {
    #[inline(always)]
    fn get_service_name(&self) -> &'static str {
        <Self as ServiceStatic<C>>::SERVICE_NAME
    }

    #[inline(always)]
    async fn serve_dyn(&self, req: APIServerReq<C>) {
        self.serve(req).await
    }
}

/// A Service mux dispatch with hashmap
///
/// Only accepts ServiceDyn register, but provide ServiceDyn and ServiceStatic for calling
pub struct ServiceMuxDyn<C: Codec> {
    map: FxHashMap<&'static str, Arc<dyn ServiceDyn<C>>>,
}

impl<C: Codec> ServiceMuxDyn<C> {
    #[inline]
    pub fn new() -> Self {
        Self { map: Default::default() }
    }

    #[inline]
    pub fn add(&mut self, service: Arc<dyn ServiceDyn<C>>) {
        self.map.insert(service.get_service_name(), service);
    }
}

impl<C: Codec> ServiceStatic<C> for ServiceMuxDyn<C> {
    const SERVICE_NAME: &'static str = "";

    #[inline(always)]
    fn serve(&self, req: APIServerReq<C>) -> impl Future<Output = ()> + Send + Sized {
        async move {
            if let Some(service) = self.map.get(req.service.as_str()) {
                service.serve_dyn(req).await
            } else {
                req.set_rpc_error(RpcIntErr::Service);
            }
        }
    }
}