Skip to main content

topcoat_runtime/
procedure.rs

1use serde::Serialize;
2use topcoat_router::Route;
3
4use crate::{Surrogate, Surrogated};
5
6/// A procedure's argument and return types for calls in runtime expressions.
7///
8/// Procedures implement [`Route`] to handle calls over HTTP. Register one
9/// with [`RouterBuilder::route`](topcoat_router::RouterBuilder::route).
10pub trait TypedProcedure: Route {
11    /// The arguments, as a tuple in declaration order.
12    type Args: Surrogated;
13
14    /// The value a successful call resolves to.
15    type Output: Surrogated;
16}
17
18/// A procedure captured by a runtime expression.
19///
20/// Serializes the endpoint URL for the browser and checks the argument and
21/// return types of [`call`](Self::call).
22pub struct ProcedureSurrogate<P> {
23    procedure: P,
24    /// The request URL, with route groups removed and `/` for the root.
25    url: &'static str,
26}
27
28impl<P: TypedProcedure> ProcedureSurrogate<P> {
29    /// Pairs a procedure with the URL that serves its calls.
30    #[must_use]
31    pub const fn new(procedure: P, url: &'static str) -> Self {
32        Self { procedure, url }
33    }
34
35    /// Invokes the procedure from the client side.
36    ///
37    /// # Panics
38    ///
39    /// Always panics; procedures can only be invoked from the client runtime.
40    #[allow(clippy::unused_async)]
41    pub async fn call(
42        &self,
43        _args: <P::Args as Surrogated>::Surrogate,
44    ) -> <P::Output as Surrogated>::Surrogate {
45        panic!("procedures cannot be executed on the server");
46    }
47}
48
49impl<P> Surrogate for &'static ProcedureSurrogate<P>
50where
51    P: TypedProcedure + Copy + Surrogated<Surrogate = Self>,
52{
53    type Real = P;
54
55    fn into_real(self) -> Self::Real {
56        self.procedure
57    }
58}
59
60impl<P: TypedProcedure> Serialize for ProcedureSurrogate<P> {
61    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
62    where
63        S: serde::Serializer,
64    {
65        #[derive(Serialize)]
66        struct TaggedProcedure {
67            t: &'static str,
68            path: &'static str,
69        }
70
71        TaggedProcedure {
72            t: "Procedure",
73            path: self.url,
74        }
75        .serialize(serializer)
76    }
77}