quic_rpc/
macros.rs

1//! Macros to reduce boilerplate for RPC implementations.
2
3/// Derive a set of RPC types and message implementation from a declaration.
4///
5/// The macros are completely optional. They generate the request and response
6/// message enums and the service zerosized struct.
7/// Optionally, a function can be created to dispatch RPC calls to methods
8/// on a struct of your choice.
9/// It can also create a type-safe RPC client for the service.
10///
11/// Usage is as follows:
12///
13/// ```no_run
14/// # use serde::{Serialize,Deserialize};
15/// # use quic_rpc::*;
16///
17/// // Define your message types
18///
19/// #[derive(Debug, Serialize, Deserialize)]
20/// struct Add(pub i32, pub i32);
21/// #[derive(Debug, Serialize, Deserialize)]
22/// pub struct Sum(pub i32);
23/// #[derive(Debug, Serialize, Deserialize)]
24/// pub struct Multiply(pub i32);
25/// #[derive(Debug, Serialize, Deserialize)]
26/// pub struct MultiplyUpdate(pub i32);
27/// #[derive(Debug, Serialize, Deserialize)]
28/// pub struct MultiplyOutput(pub i32);
29///
30/// // Derive the RPC types.
31///
32/// rpc_service! {
33///     // Name of the created request enum.
34///     Request = MyRequest;
35///     // Name of the created response enum.
36///     Response = MyResponse;
37///     // Name of the created service struct enum.
38///     Service = MyService;
39///     // Name of the macro to create a dispatch function.
40///     // Optional, if not needed pass _ (underscore) as name.
41///     CreateDispatch = create_my_dispatch;
42///     // Name of the macro to create an RPC client.
43///
44///     Rpc add = Add, _ -> Sum;
45///     BidiStreaming multiply = Multiply, MultiplyUpdate -> MultiplyOutput
46/// }
47/// ```
48///
49/// This will generate a request enum `MyRequest`, a response enum `MyRespone`
50/// and a service declaration `MyService`.
51///
52/// It will also generate two macros to create an RPC client and a dispatch function.
53///
54/// To use the client, invoke the macro with a name. The macro will generate a struct that
55/// takes a client channel and exposes typesafe methods for each RPC method.
56///
57/// ```ignore
58/// create_store_client!(MyClient);
59/// let client = quic_rpc::quinn::Channel::new(client);
60/// let client = quic_rpc::client::RpcClient::<MyService, _>::new(client);
61/// let mut client = MyClient(client);
62/// let sum = client.add(Add(3, 4)).await?;
63/// // Sum(7)
64/// let (send, mut recv) = client.multiply(Multiply(2));
65/// send(Update(3));
66/// let res = recv.next().await?;
67/// // Some(MultiplyOutput(6))
68/// ```
69///
70/// To use the dispatch function, invoke the macro with a struct that implements your RPC
71/// methods and the name of the generated function. You can then use this dispatch function
72/// to dispatch the RPC calls to the methods on your target struct.
73///
74/// ```ignore
75/// #[derive(Clone)]
76/// pub struct Calculator;
77/// impl Calculator {
78///     async fn add(self, req: Add) -> Sum {
79///         Sum(req.0 + req.1)
80///     }
81///     async fn multiply(
82///         self,
83///         req: Multiply,
84///         updates: impl Stream<Item = MultiplyUpdate>
85///     ) -> impl Stream<Item = MultiplyOutput> {
86///        stream! {
87///            tokio::pin!(updates);
88///            while let Some(MultiplyUpdate(n)) = updates.next().await {
89///                yield MultiplyResponse(req.0 * n);
90///            }
91///        }
92///     }
93/// }
94///
95/// create_my_dispatch!(Calculator, dispatch_calculator_request);
96///
97/// #[tokio::main]
98/// async fn main() -> anyhow::Result<()> {
99///    let server_addr: std::net::SocketAddr = "127.0.0.1:12345".parse()?;
100///    let (server, _server_certs) = make_server_endpoint(server_addr)?;
101///    let accept = server.accept().await.context("accept failed")?.await?;
102///    let connection = quic_rpc::quinn::Channel::new(accept);
103///    let calculator = Calculator;
104///    let server_handle = spawn_server(
105///        StoreService,
106///        quic_rpc::quinn::QuinnChannelTypes,
107///        connection,
108///        calculator,
109///        dispatch_calculator_request,
110///    );
111///    server_handle.await??;
112///    Ok(())
113/// }
114/// ```
115///
116/// The generation of the macros in `CreateDispatch` and `CreateClient`
117/// is optional. If you don't need them, pass `_` instead:
118///
119/// ```ignore
120/// # use quic_rpc::*;
121/// rpc_service! {
122///     Request = MyRequest;
123///     Response = MyResponse;
124///     Service = MyService;
125///     CreateDispatch = _;
126///     CreateClient = _;
127///
128///     Rpc add = Add, _ -> Sum;
129///     ClientStreaming stream = Input, Update -> Output;
130/// }
131/// ```
132/// `
133#[macro_export]
134macro_rules! rpc_service {
135    (
136        Request = $request:ident;
137        Response = $response:ident;
138        Service = $service:ident;
139        CreateDispatch = $create_dispatch:tt;
140
141        $($m_pattern:ident $m_name:ident = $m_input:ident, $m_update:tt -> $m_output:ident);+$(;)?
142    ) => {
143
144        $crate::__request_enum! {
145            $service,
146            $request {
147                $($m_input,)*
148                $($m_update,)*
149            }
150        }
151
152        #[doc=concat!("Response messages for ", stringify!($service))]
153        #[allow(clippy::enum_variant_names)]
154        #[derive(::std::fmt::Debug, ::derive_more::From, ::derive_more::TryInto, ::serde::Serialize, ::serde::Deserialize)]
155        pub enum $response {
156            $($m_output($m_output),)*
157        }
158
159        $(
160            $crate::__rpc_message!($service, $m_pattern, $m_input, $m_update, $m_output);
161        )*
162
163        #[doc=concat!("RPC service ", stringify!($service))]
164        #[derive(::std::clone::Clone, ::std::fmt::Debug)]
165        pub struct $service;
166
167        impl $crate::Service for $service {
168            type Req = $request;
169            type Res = $response;
170        }
171
172        $crate::__derive_create_dispatch!(
173            $service,
174            $request,
175            $create_dispatch,
176            [ $($m_pattern $m_name = $m_input, $m_update -> $m_output);+ ]
177        );
178    };
179}
180
181#[doc(hidden)]
182#[macro_export]
183macro_rules! __derive_create_dispatch {
184    (
185        $service:ident,
186        $request:ident,
187        _,
188        [ $($tt:tt)* ]
189    ) => {};
190    (
191        $service:ident,
192        $request:ident,
193        $create_dispatch:ident,
194        [ $($m_pattern:ident $m_name:ident = $m_input:ident, $m_update:tt -> $m_output:ident);+ ]
195    ) => {
196        #[doc = concat!("Create an RPC request dispatch function for ", stringify!($service), "\n\nSee the docs for [quic_rpc::rpc_service] for usage docs.")]
197        #[macro_export]
198        macro_rules! $create_dispatch {
199            ($target:ident, $handler:ident) => {
200                pub async fn $handler<C: $crate::Listener<$service>>(
201                    mut chan: $crate::server::RpcChannel<$service, C>,
202                    msg: <$service as $crate::Service>::Req,
203                    target: $target,
204                ) -> Result<(), $crate::server::RpcServerError<C>> {
205                    let res = match msg {
206                        $(
207                            $request::$m_input(msg) => { $crate::__rpc_invoke!($m_pattern, $m_name, $target, msg, chan, target) },
208                        )*
209                        _ => Err($crate::server::RpcServerError::<C>::UnexpectedStartMessage),
210                    };
211                    res?;
212                    Ok(())
213                }
214            }
215        }
216    };
217}
218
219#[doc(hidden)]
220#[macro_export]
221macro_rules! __request_enum {
222    // User entry points.
223    ($service:ident, $enum_name:ident { $variant_name:ident $($tt:tt)* }) => {
224        $crate::__request_enum!(@ {[$service $enum_name] [$variant_name]} $($tt)*);
225    };
226
227    // Internal rules to categorize each value
228    // This also filters out _ placeholders from non-streaming methods.
229    (@ {[$service:ident $enum_name:ident] [$($agg:ident)*]} $(,)? $(_$(,)?)* $variant_name:ident $($tt:tt)*) => {
230        $crate::__request_enum!(@ {[$service $enum_name] [$($agg)* $variant_name]} $($tt)*);
231    };
232
233    // Internal rules to categorize each value
234    (@ {[$service:ident $enum_name:ident] [$($agg:ident)*]} $(,)? $variant_name:ident $($tt:tt)*) => {
235        $crate::__request_enum!(@ {[$service $enum_name] [$($agg)* $variant_name]} $($tt)*);
236    };
237
238    // Final internal rule that generates the enum from the categorized input
239    (@ {[$service:ident $enum_name:ident] [$($n:ident)*]} $(,)? $(_$(,)?)*) => {
240        #[doc=concat!("Request messages for ", stringify!($service))]
241        #[derive(::std::fmt::Debug, ::derive_more::From, ::derive_more::TryInto, ::serde::Serialize, ::serde::Deserialize)]
242        pub enum $enum_name {
243            $($n($n),)*
244        }
245    };
246}
247
248/// Declare a message to be a rpc message for a service.
249///
250/// Example:
251/// ```ignore
252/// declare_rpc!(TestService, TestRequest, TestResponse);
253/// ```
254///
255/// This is equivalent to:
256/// ```ignore
257/// impl RpcMsg<TestService> for TestRequest {
258///    type Response = TestResponse;
259/// }
260/// ```
261#[macro_export]
262macro_rules! declare_rpc {
263    ($service:ty, $m_input:ty, $m_output:ty) => {
264        impl $crate::message::RpcMsg<$service> for $m_input {
265            type Response = $m_output;
266        }
267    };
268}
269
270/// Declare a message to be a server streaming message for a service.
271///
272/// Example:
273/// ```ignore
274/// declare_server_streaming!(TestService, TestRequest, TestResponse);
275/// ```
276///
277/// This is equivalent to:
278/// ```ignore
279/// impl Msg<TestService> for TestRequest {
280///     type Pattern = ServerStreamingPattern;
281/// }
282///
283/// impl ServerStreamingMsg<TestService> for TestRequest {
284///     type Response = TestResponse;
285/// }
286#[macro_export]
287macro_rules! declare_server_streaming {
288    ($service:ident, $m_input:ident, $m_output:ident) => {
289        impl $crate::message::Msg<$service> for $m_input {
290            type Pattern = $crate::message::ServerStreaming;
291        }
292        impl $crate::message::ServerStreamingMsg<$service> for $m_input {
293            type Response = $m_output;
294        }
295    };
296}
297
298/// Declare a message to be a server streaming message for a service.
299///
300/// Example:
301/// ```ignore
302/// declare_client_streaming!(TestService, TestRequest, TestUpdate, TestResponse);
303/// ```
304///
305/// This is equivalent to:
306/// ```ignore
307/// impl Msg<TestService> for TestRequest {
308///     type Pattern = ClientStreamingPattern;
309/// }
310///
311/// impl ClientStreamingMsg<TestService> for TestRequest {
312///     type Update = TestUpdate;
313///     type Response = TestResponse;
314/// }
315/// ```
316#[macro_export]
317macro_rules! declare_client_streaming {
318    ($service:ident, $m_input:ident, $m_update:ident, $m_output:ident) => {
319        impl $crate::message::Msg<$service> for $m_input {
320            type Pattern = $crate::message::ClientStreaming;
321        }
322        impl $crate::message::ClientStreamingMsg<$service> for $m_input {
323            type Update = $m_update;
324            type Response = $m_output;
325        }
326    };
327}
328
329/// Declare a message to be a server streaming message for a service.
330///
331/// Example:
332/// ```ignore
333/// declare_bidi_streaming!(TestService, TestRequest, TestUpdate, TestResponse);
334/// ```
335///
336/// This is equivalent to:
337/// ```ignore
338/// impl Msg<TestService> for TestRequest {
339///     type Pattern = BidiStreamingPattern;
340/// }
341///
342/// impl BidiStreamingMsg<TestService> for TestRequest {
343///     type Update = TestUpdate;
344///     type Response = TestResponse;
345/// }
346/// ```
347#[macro_export]
348macro_rules! declare_bidi_streaming {
349    ($service:ident, $m_input:ident, $m_update:ident, $m_output:ident) => {
350        impl $crate::message::Msg<$service> for $m_input {
351            type Pattern = $crate::message::BidiStreaming;
352        }
353        impl $crate::message::BidiStreamingMsg<$service> for $m_input {
354            type Update = $m_update;
355            type Response = $m_output;
356        }
357    };
358}
359
360#[doc(hidden)]
361#[macro_export]
362macro_rules! __rpc_message {
363    ($service:ident, Rpc, $m_input:ident, _, $m_output:ident) => {
364        impl $crate::message::RpcMsg<$service> for $m_input {
365            type Response = $m_output;
366        }
367    };
368    ($service:ident, ServerStreaming, $m_input:ident, _, $m_output:ident) => {
369        impl $crate::message::Msg<$service> for $m_input {
370            type Pattern = $crate::message::ServerStreaming;
371        }
372        impl $crate::message::ServerStreamingMsg<$service> for $m_input {
373            type Response = $m_output;
374        }
375    };
376    ($service:ident, ClientStreaming, $m_input:ident, $m_update:ident, $m_output:ident) => {
377        impl $crate::message::Msg<$service> for $m_input {
378            type Pattern = $crate::message::ClientStreaming;
379        }
380        impl $crate::message::ClientStreamingMsg<$service> for $m_input {
381            type Response = $m_output;
382            type Update = $m_update;
383        }
384    };
385    ($service:ident, BidiStreaming, $m_input:ident, $m_update:ident, $m_output:ident) => {
386        impl $crate::message::Msg<$service> for $m_input {
387            type Pattern = $crate::message::BidiStreaming;
388        }
389        impl $crate::message::BidiStreamingMsg<$service> for $m_input {
390            type Response = $m_output;
391            type Update = $m_update;
392        }
393    };
394}
395
396#[doc(hidden)]
397#[macro_export]
398macro_rules! __rpc_invoke {
399    (Rpc, $m_name:ident, $target_ty:ident, $msg:ident, $chan:ident, $target:ident) => {
400        $chan.rpc($msg, $target, $target_ty::$m_name).await
401    };
402    (ClientStreaming, $m_name:ident, $target_ty:ident, $msg:ident, $chan:ident, $target:ident) => {
403        $chan
404            .client_streaming($msg, $target, $target_ty::$m_name)
405            .await
406    };
407    (ServerStreaming, $m_name:ident, $target_ty:ident, $msg:ident, $chan:ident, $target:ident) => {
408        $chan
409            .server_streaming($msg, $target, $target_ty::$m_name)
410            .await
411    };
412    (BidiStreaming, $m_name:ident, $target_ty:ident, $msg:ident, $chan:ident, $target:ident) => {
413        $chan
414            .bidi_streaming($msg, $target, $target_ty::$m_name)
415            .await
416    };
417}
418
419#[doc(hidden)]
420#[macro_export]
421macro_rules! __derive_create_client{
422    (
423        $service:ident,
424        _,
425        [ $($tt:tt)* ]
426    ) => {};
427    (
428        $service:ident,
429        $create_client:tt,
430        [ $($m_pattern:ident $m_name:ident = $m_input:ident, $m_update:tt -> $m_output:ident);+ ]
431    ) => {
432        #[doc = concat!("Create an RPC client for ", stringify!($service), "\n\nSee the docs for [quic_rpc::rpc_service] for usage docs.")]
433        #[macro_export]
434        macro_rules! $create_client {
435            ($struct:ident) => {
436                #[derive(::std::clone::Clone, ::std::fmt::Debug)]
437                pub struct $struct<C: $crate::Listener<$service>>(pub $crate::client::RpcClient<$service, C>);
438
439                impl<C: $crate::Listener<$service>> $struct<C> {
440                    $(
441                        $crate::__rpc_method!($m_pattern, $service, $m_name, $m_input, $m_output, $m_update);
442                    )*
443                }
444            };
445        }
446    };
447}
448
449#[doc(hidden)]
450#[macro_export]
451macro_rules! __rpc_method {
452    (Rpc, $service:ident, $m_name:ident, $m_input:ident, $m_output:ident, _) => {
453        pub async fn $m_name(
454            &mut self,
455            input: $m_input,
456        ) -> ::std::result::Result<$m_output, $crate::client::RpcClientError<C>> {
457            self.0.rpc(input).await
458        }
459    };
460    (ClientStreaming, $service:ident, $m_name:ident, $m_input:ident, $m_output:ident, $m_update:ident) => {
461        pub async fn $m_name(
462            &mut self,
463            input: $m_input,
464        ) -> ::std::result::Result<
465            (
466                $crate::client::UpdateSink<$service, C, $m_input>,
467                ::futures::future::BoxFuture<
468                    'static,
469                    ::std::result::Result<$m_output, $crate::client::ClientStreamingItemError<C>>,
470                >,
471            ),
472            $crate::client::ClientStreamingError<C>,
473        > {
474            self.0.client_streaming(input).await
475        }
476    };
477    (ServerStreaming, $service:ident, $m_name:ident, $m_input:ident, $m_output:ident, _) => {
478        pub async fn $m_name(
479            &mut self,
480            input: $m_input,
481        ) -> ::std::result::Result<
482            ::futures::stream::BoxStream<
483                'static,
484                ::std::result::Result<$m_output, $crate::client::StreamingResponseItemError<C>>,
485            >,
486            $crate::client::StreamingResponseError<C>,
487        > {
488            self.0.server_streaming(input).await
489        }
490    };
491    (BidiStreaming, $service:ident, $m_name:ident, $m_input:ident, $m_output:ident, $m_update:ident) => {
492        pub async fn $m_name(
493            &mut self,
494            input: $m_input,
495        ) -> ::std::result::Result<
496            (
497                $crate::client::UpdateSink<$service, C, $m_input>,
498                ::futures::stream::BoxStream<
499                    'static,
500                    ::std::result::Result<$m_output, $crate::client::BidiItemError<C>>,
501                >,
502            ),
503            $crate::client::BidiError<C>,
504        > {
505            self.0.bidi(input).await
506        }
507    };
508}