Skip to main content

razor_rpc/server/
service.rs

1use super::task::APIServerReq;
2use crate::{Codec, error::RpcIntErr};
3use ahash::AHashMap;
4use std::sync::Arc;
5
6/// Service handling code generated by macro
7///
8/// A ServiceStatic can be acquired by:
9/// - `Arc<T>` where `T: ServiceStatic`,
10/// - attr macro [service](crate::server::service): a class with multi method, if only one service, ignore [APIServerReq::service].
11/// - attr macro [service_mux_struct](crate::server::service_mux_struct): a struct that contains several service, match [APIServerReq::service] by [ServiceStatic::SERVICE_NAME] field,
12/// - [ServiceMuxDyn]: match multiple `dyn ServiceDyn` with SERVICE_NAME  (ServiceStatic auto impl [ServiceDyn])
13pub trait ServiceStatic<C: Codec>: Send + Sync + 'static + Sized {
14    /// For server_mux_struct to match
15    const SERVICE_NAME: &'static str;
16
17    /// the [`#[service]`](crate::server::service) macro should generate code like this:
18    /// ```no_compile
19    /// match req.method
20    ///     match req.decode::<RequestType>() {
21    ///         Err(())=>{
22    ///             req.set_rpc_error(razor_rpc_core::error::RpcIntErr::Decode);
23    ///             returnl
24    ///         }
25    ///         Ok(arg)=>{
26    ///             match self.#method(arg).await {
27    ///                 Ok(resp)=>{
28    ///                     req.set_result(resp);
29    ///                 }
30    ///                 Err(e)=>{
31    ///                     req.set_error::<E: RpcErrCodec>(e)
32    ///                 }
33    ///             }
34    ///         }
35    ///     }
36    /// ```
37    fn serve(&self, req: APIServerReq<C>) -> impl Future<Output = ()> + Send + Sized;
38}
39
40impl<S: ServiceStatic<C>, C: Codec> ServiceStatic<C> for Arc<S> {
41    const SERVICE_NAME: &'static str = S::SERVICE_NAME;
42
43    #[inline(always)]
44    fn serve(&self, req: APIServerReq<C>) -> impl Future<Output = ()> + Send + Sized {
45        self.as_ref().serve(req)
46    }
47}
48
49/// Service trait for dyn dispatch, used by [ServiceMuxDyn]
50///
51/// NOTE:
52/// - [ServiceStatic] auto impl `ServiceDyn`
53/// - It cannot be used by [service_mux_struct](crate::server::service_mux_struct)
54#[async_trait::async_trait]
55pub trait ServiceDyn<C: Codec>: Send + Sync + 'static {
56    fn get_service_name(&self) -> &'static str;
57
58    async fn serve_dyn(&self, req: APIServerReq<C>);
59}
60
61#[async_trait::async_trait]
62impl<S: ServiceStatic<C>, C: Codec> ServiceDyn<C> for S {
63    #[inline(always)]
64    fn get_service_name(&self) -> &'static str {
65        <Self as ServiceStatic<C>>::SERVICE_NAME
66    }
67
68    #[inline(always)]
69    async fn serve_dyn(&self, req: APIServerReq<C>) {
70        self.serve(req).await
71    }
72}
73
74/// A Service mux dispatch with hashmap
75///
76/// Only accepts ServiceDyn register, but provide ServiceDyn and ServiceStatic for calling
77pub struct ServiceMuxDyn<C: Codec> {
78    map: AHashMap<&'static str, Arc<dyn ServiceDyn<C>>>,
79}
80
81impl<C: Codec> Default for ServiceMuxDyn<C> {
82    fn default() -> Self {
83        Self { map: Default::default() }
84    }
85}
86
87impl<C: Codec> ServiceMuxDyn<C> {
88    #[inline]
89    pub fn new() -> Self {
90        Default::default()
91    }
92
93    #[inline]
94    pub fn add(&mut self, service: Arc<dyn ServiceDyn<C>>) {
95        self.map.insert(service.get_service_name(), service);
96    }
97}
98
99impl<C: Codec> ServiceStatic<C> for ServiceMuxDyn<C> {
100    const SERVICE_NAME: &'static str = "";
101
102    #[inline(always)]
103    async fn serve(&self, req: APIServerReq<C>) {
104        if let Some(service) = self.map.get(req.service.as_str()) {
105            service.serve_dyn(req).await
106        } else {
107            req.set_rpc_error(RpcIntErr::Service);
108        }
109    }
110}