Skip to main content

workflow_rpc/server/interface/
method.rs

1//! Module containing RPC [`Method`] closure wrappers
2use crate::imports::*;
3
4/// Base trait representing an RPC method, used to retain
5/// method structures in an [`Interface`](super::Interface)
6/// map without generics.
7#[async_trait]
8pub(crate) trait MethodTrait<ServerContext, ConnectionContext>:
9    Send + Sync + 'static
10{
11    async fn call_with_borsh(
12        &self,
13        server_ctx: ServerContext,
14        connection_ctx: ConnectionContext,
15        data: &[u8],
16    ) -> ServerResult<Vec<u8>>;
17    async fn call_with_serde_json(
18        &self,
19        server_ctx: ServerContext,
20        connection_ctx: ConnectionContext,
21        value: Value,
22    ) -> ServerResult<Value>;
23}
24
25/// RPC method function type
26pub type MethodFn<ServerContext, ConnectionContext, Req, Resp> = Arc<
27    Box<
28        dyn Send
29            + Sync
30            + Fn(ServerContext, ConnectionContext, Req) -> MethodFnReturn<Resp>
31            + 'static,
32    >,
33>;
34
35/// RPC method function return type
36pub type MethodFnReturn<T> = Pin<Box<dyn Send + 'static + Future<Output = ServerResult<T>>>>;
37
38/// RPC method wrapper. Contains the method closure function.
39pub struct Method<ServerContext, ConnectionContext, Req, Resp>
40where
41    ServerContext: Send + Sync + 'static,
42    Req: MsgT,
43    Resp: MsgT,
44{
45    method: MethodFn<ServerContext, ConnectionContext, Req, Resp>,
46}
47
48impl<ServerContext, ConnectionContext, Req, Resp>
49    Method<ServerContext, ConnectionContext, Req, Resp>
50where
51    ServerContext: Send + Sync + 'static,
52    Req: MsgT,
53    Resp: MsgT,
54{
55    /// Wrap a method handler closure into a [`Method`], taking the server and
56    /// connection contexts plus a typed request and returning a typed response.
57    pub fn new<FN>(method_fn: FN) -> Method<ServerContext, ConnectionContext, Req, Resp>
58    where
59        FN: Send
60            + Sync
61            + Fn(ServerContext, ConnectionContext, Req) -> MethodFnReturn<Resp>
62            + 'static,
63    {
64        Method {
65            method: Arc::new(Box::new(method_fn)),
66        }
67    }
68}
69
70#[async_trait]
71impl<ServerContext, ConnectionContext, Req, Resp> MethodTrait<ServerContext, ConnectionContext>
72    for Method<ServerContext, ConnectionContext, Req, Resp>
73where
74    ServerContext: Clone + Send + Sync + 'static,
75    ConnectionContext: Clone + Send + Sync + 'static,
76    Req: MsgT,
77    Resp: MsgT,
78{
79    async fn call_with_borsh(
80        &self,
81        server_ctx: ServerContext,
82        connection_ctx: ConnectionContext,
83        data: &[u8],
84    ) -> ServerResult<Vec<u8>> {
85        let req = Req::try_from_slice(data)?;
86        let resp = (self.method)(server_ctx, connection_ctx, req).await;
87        let vec = borsh::to_vec(&resp)?;
88        Ok(vec)
89    }
90
91    async fn call_with_serde_json(
92        &self,
93        server_ctx: ServerContext,
94        connection_ctx: ConnectionContext,
95        value: Value,
96    ) -> ServerResult<Value> {
97        let req: Req = serde_json::from_value(value).map_err(|_| ServerError::ReqDeserialize)?;
98        let resp = (self.method)(server_ctx, connection_ctx, req).await?;
99        Ok(serde_json::to_value(resp).map_err(|_| ServerError::RespSerialize)?)
100    }
101}