rpc_rs/proc/
mod.rs

1//! The procedure module.
2
3use std::{borrow::Cow, future::Future};
4
5use async_trait::async_trait;
6use serde::{de::DeserializeOwned, Serialize};
7use serde_json::Error;
8use specta::{function::FunctionDataType, Type, TypeMap};
9
10use crate::util::TripleS;
11
12/// A generic procedure. This is required to allow for any procedure
13/// to be used in a [`rpc_rs::Module`].
14#[async_trait]
15pub trait GenericProcedure<Cx: Send + Sync> {
16    /// Run this procedure with the context and a JSON-encoded string
17    /// representing the data required.
18    async fn run(&self, cx: Cx, data: String) -> Result<String, Error>;
19
20    /// Get the [`FunctionDataType`] of this procedure.
21    fn specta_type(&self, name: Cow<'static, str>, map: &mut TypeMap) -> FunctionDataType;
22}
23
24#[async_trait]
25impl<
26        Cx: TripleS + Clone,
27        Output: Send + Sync + Serialize + Type,
28        Arg: Send + Sync + 'static + DeserializeOwned + Type,
29    > GenericProcedure<Cx> for WrappedProcedure<Cx, Output, Arg>
30{
31    async fn run(&self, cx: Cx, mut data: String) -> Result<String, Error> {
32        if data.is_empty() {
33            data = String::from("null");
34        }
35
36        serde_json::to_string(&self.0.exec(cx, serde_json::from_str(&data)?).await)
37    }
38
39    fn specta_type(&self, name: Cow<'static, str>, map: &mut TypeMap) -> FunctionDataType {
40        self.0.specta_type(name, map)
41    }
42}
43
44/// A generic-wrapped [`Procedure`].
45///
46/// This is necessary so the [`GenericProcedure`] trait can be
47/// implemented in an object-safe manner.
48pub struct WrappedProcedure<
49    Cx: TripleS + Clone,
50    Output: Send + Sync + Serialize + Type,
51    Arg: Send + Sync + 'static + DeserializeOwned + Type,
52>(Box<dyn Procedure<Cx, Output, Arg> + Send + Sync>);
53
54/// A procedure.
55#[async_trait]
56pub trait Procedure<
57    Cx: TripleS + Clone,
58    Output: Send + Sync + Serialize + Type,
59    Arg: Send + Sync + DeserializeOwned + Type,
60>
61{
62    /// Run this procedure with its context and data.
63    async fn exec(&self, cx: Cx, data: Arg) -> Output;
64}
65
66/// A typed extension to [`Procedure`].
67pub trait TypedProcedure<
68    Cx: TripleS + Clone,
69    Output: Send + Sync + Serialize + Type,
70    Arg: Send + Sync + DeserializeOwned + Type,
71>: Procedure<Cx, Output, Arg>
72{
73    /// Get the [`FunctionDataType`] of this procedure.
74    fn specta_type(&self, name: Cow<'static, str>, map: &mut TypeMap) -> FunctionDataType;
75}
76
77#[async_trait]
78impl<
79        Cx: TripleS + Clone,
80        Output: Send + Sync + Serialize + Type,
81        Fut: Future<Output = Output> + Send,
82        Arg: Send + Sync + 'static + DeserializeOwned + Type,
83        Func: (Fn(Cx, Arg) -> Fut) + Send + Sync + 'static,
84    > Procedure<Cx, Output, Arg> for Func
85{
86    async fn exec(&self, cx: Cx, data: Arg) -> Output {
87        self(cx, data).await
88    }
89}
90
91impl<
92        Cx: TripleS + Clone,
93        Output: Send + Sync + Serialize + Type,
94        Arg: Send + Sync + 'static + DeserializeOwned + Type,
95        Proc: Procedure<Cx, Output, Arg> + ?Sized,
96    > TypedProcedure<Cx, Output, Arg> for Proc
97{
98    fn specta_type(&self, name: Cow<'static, str>, map: &mut TypeMap) -> FunctionDataType {
99        FunctionDataType {
100            args: vec![("data".into(), Arg::reference(map, &[]).inner)],
101            asyncness: true,
102            deprecated: None,
103            name,
104            docs: "".into(),
105            result: Some(Output::reference(map, &[]).inner),
106        }
107    }
108}
109
110/// Wrap any [`Procedure`], converting it to a [`WrappedProcedure`]
111/// which implements the [`GenericProcedure`] trait.
112pub fn wrap<
113    Cx: TripleS + Clone,
114    Output: Send + Sync + Serialize + Type,
115    Arg: Send + Sync + 'static + DeserializeOwned + Type,
116    Proc: Procedure<Cx, Output, Arg> + Send + Sync + 'static,
117>(
118    proc: Proc,
119) -> WrappedProcedure<Cx, Output, Arg> {
120    WrappedProcedure(Box::new(proc))
121}