fizyr_rpc/macros.rs
1//! Extra macro documentation.
2
3#[doc(hidden)]
4pub use fizyr_rpc_macros::interface as interface_impl;
5
6#[macro_export]
7/// Define an RPC interface.
8///
9/// This macro generates an `Interface, `Client` and `Server` struct, and some more helper types.
10///
11/// The `Interface` struct is used to enable RPC interface introspection.
12/// Currently, it only supports retrieving the documentation of the interface itself.
13/// In the future, you can use this struct to get a list of services and streaming messages too.
14///
15/// The client struct is used to initiate requests and send stream messages.
16/// It can be created from a [`PeerWriteHandle`] or a [`PeerHandle`].
17/// Note that if you create the client from a [`PeerHandle`], the [`PeerReadHandle`] will not be accessible.
18///
19/// For the server struct it is exactly the opposite: it is used to receive requests and stream messages.
20/// It can be created from a [`PeerReadHandle`] or a [`PeerHandle`],
21/// but creating it from a [`PeerHandle`] will discard the [`PeerWriteHandle`].
22///
23/// # Example
24///
25/// See the [`interface_example`] module for an example, with the source code and generated documentation.
26///
27/// # Syntax
28///
29/// The syntax for the macro is as follows:
30/// ```no_compile
31/// fizyr_rpc::interface! {
32/// // The `interface` keyword defines an RPC interface.
33/// // You must have exactly one interface definition in the macro invocation.
34/// //
35/// // The $interface_name is used in automatically generated documentation and should be UpperCamelCase.
36/// //
37/// // You can adjust the visiblity of the generated items with the `pub` keyword as normal.
38/// // By default, all generated items are private.
39/// //
40/// // Each item can have user written documentation.
41/// // Simply write doc comments with triple slashes as usual.
42/// // This applies to the interface definition, service definitions, update definitions and stream definitions.
43/// //
44/// // The documentation writter on the `interface` item can be retrieved through the introspection API,
45/// // but does not appear in rustdoc.
46/// pub interface $interface_name {
47/// // The `service` keyword defines a service.
48/// //
49/// // You can have any amount of service definitions inside an interface definition.
50/// //
51/// // The $id is used as the service ID and must be an i32.
52/// // The ID must be unique for all services in the interface.
53/// //
54/// // The $name is the name of the service.
55/// // It is used to generate function and type names.
56/// // It must be a valid Rust identifier and should be lowercase with underscores.
57/// //
58/// // The $request_type and $response_type indicate the message body for the request and the response.
59/// // If there is no data in a request or response, you can use the unit type: `()`
60/// //
61/// // If the service has no update messages, you can end the definition with a comma.
62/// // See the next item for the syntax of services with update messages.
63/// service $id $name: $request_type -> $response_type,
64///
65/// // If a service has update messages, you can declare them in the service block.
66/// service $id $name: $request_type -> $response_type {
67/// // The `request_update` keyword defines a request update.
68/// // You can have any amount of request updates inside a service definition.
69/// //
70/// // The $id is used as the service ID for the update and must be an i32.
71/// // The ID must be unique for all request updates in the service.
72/// //
73/// // The $name is the name of the update message.
74/// // It is used to generate function and type names.
75/// // It must be a valid Rust identifier and should be lowercase with underscores.
76/// //
77/// // The $body_type indicates the type of the message.
78/// // If there is no data in the message, you can use the unit type: `()`
79/// request_update $id $name: $body_type,
80///
81/// // The `response_update` keyword defines a response update.
82/// // You can have any amount of response updates inside a service definition.
83/// //
84/// // The $id is used as the service ID for the update and must be an i32.
85/// // The ID must be unique for all response updates in the service.
86/// //
87/// // The $name is the name of the update message.
88/// // It is used to generate function and type names.
89/// // It must be a valid Rust identifier and should be lowercase with underscores.
90/// //
91/// // The $body_type indicates the type of the message.
92/// // If there is no data in the message, you can use the unit type: `()`
93/// response_update $id $name: $body_type,
94/// }
95///
96/// // The `stream` keyword defines a stream message.
97/// // You can have any amount of stream definitions in an interface definition.
98/// //
99/// // The $id is used as the service ID of the stream message and must be an i32.
100/// // The ID must be unique for all streams in the interface.
101/// //
102/// // The $name is the name of the stream.
103/// // It is used to generate function and type names.
104/// // It must be a valid Rust identifier and should be lowercase with underscores.
105/// //
106/// // The $body_type indicates the type of the message.
107/// // If there is no data in the message, you can use the unit type: `()`
108/// stream $id $name: $body_type,
109/// }
110/// }
111/// ```
112///
113/// [`PeerHandle`]: crate::PeerHandle
114/// [`PeerWriteHandle`]: crate::PeerWriteHandle
115/// [`PeerReadHandle`]: crate::PeerReadHandle
116macro_rules! interface {
117 ($($tokens:tt)*) => {
118 $crate::macros::interface_impl!{$crate; $($tokens)*}
119 }
120}
121
122/// Example module for the `interface!` macro.
123///
124/// You can compare the source of the generated documentation to inspect the generated API.
125/// The most important generated types are [`Client`] and [`Server`].
126///
127/// [`Client`]: interface_example::Client
128/// [`Server`]: interface_example::Server
129///
130/// ```no_compile
131/// fizyr_rpc::interface! {
132/// /// RPC interface for the supermarket.
133/// pub interface Supermarket {
134/// /// Greet the cashier.
135/// ///
136/// /// The cashier will reply with their own greeting.
137/// service 1 greet_cashier: String -> String,
138///
139/// /// Purchase tomatoes.
140/// ///
141/// /// The response of the cashier depends on the update messages exchanged.
142/// /// If you run away after they have sent the `price`, or if you pay with a nun-fungable token,
143/// /// they will respond with [`BuyTomatoesResponse::ICalledSecurity`].
144/// ///
145/// /// If you pay with the correct amount, they will respond with [`BuyTomatoesResponse::ThankYouComeAgain`].
146/// service 2 buy_tomatoes: BuyTomatoesRequest -> BuyTomatoesResponse {
147/// /// Sent once by the cashier to notify you of the price of the tomatoes.
148/// response_update 1 price: Price,
149///
150/// /// Sent by the client to pay for the tomatoes.
151/// request_update 1 pay: Payment,
152///
153/// /// Sent by broke or kleptomanic clients that still want tomatoes.
154/// request_update 2 run_away: (),
155/// }
156///
157/// /// Mutter something as you're walking through the supermarket.
158/// ///
159/// /// Since no-one will respond, this is a stream rather than a service.
160/// ///
161/// /// Popular phrases include:
162/// /// * Woah thats cheap!
163/// /// * Everything used to be better in the good old days...
164/// /// * Why did they move the toilet paper?
165/// stream 1 mutter: String,
166/// }
167/// }
168///
169/// /// The initial request to buy tomatoes.
170/// #[derive(Debug)]
171/// pub struct BuyTomatoesRequest {
172/// /// The tomatoes you want to buy.
173/// pub amount: usize,
174/// }
175///
176/// /// The price for something.
177/// #[derive(Debug)]
178/// pub struct Price {
179/// /// The total price in cents.
180/// pub total_price_cents: usize,
181/// }
182///
183/// /// Payment options for purchasing tomatoes.
184/// #[derive(Debug)]
185/// pub enum Payment {
186/// /// Payment in money.
187/// Money {
188/// /// The amount of money in cents.
189/// cents: usize
190/// },
191///
192/// /// Payment with an NFT.
193/// NonFungibleToken,
194/// }
195///
196/// /// The response of a cashier when buying tomatoes.
197/// #[derive(Debug)]
198/// pub enum BuyTomatoesResponse {
199/// /// A final greeting and your receipt.
200/// ThankYouComeAgain(Receipt),
201///
202/// /// Security has been called.
203/// ICalledSecurity,
204/// }
205///
206/// /// A receipt for your purchase.
207/// #[derive(Debug)]
208/// pub struct Receipt {
209/// /// The number of tomatoes you bought.
210/// pub amount_of_tomatoes: usize,
211///
212/// /// The total price you paid for the tomatoes.
213/// pub total_price_cents: usize,
214///
215/// /// If the cashier really liked you, they may write their phone number on the receipt with pen.
216/// pub phone_number: Option<String>,
217/// }
218/// ```
219pub mod interface_example {
220 interface! {
221 /// RPC interface for the supermarket.
222 pub interface Supermarket {
223 /// Greet the cashier.
224 ///
225 /// The cashier will reply with their own greeting.
226 service 1 greet_cashier: String -> String,
227
228 /// Purchase tomatoes.
229 ///
230 /// The response of the cashier depends on the update messages exchanged.
231 /// If you run away after they have sent the `price`, or if you pay with a nun-fungable token,
232 /// they will respond with [`BuyTomatoesResponse::ICalledSecurity`].
233 ///
234 /// If you pay with the correct amount, they will respond with [`BuyTomatoesResponse::ThankYouComeAgain`].
235 service 2 buy_tomatoes: BuyTomatoesRequest -> BuyTomatoesResponse {
236 /// Sent once by the cashier to notify you of the price of the tomatoes.
237 response_update 1 price: Price,
238
239 /// Sent by the client to pay for the tomatoes.
240 request_update 1 pay: Payment,
241
242 /// Sent by broke or kleptomanic clients that still want tomatoes.
243 request_update 2 run_away: (),
244 }
245
246 /// Mutter something as you're walking through the supermarket.
247 ///
248 /// Since no-one will respond, this is a stream rather than a service.
249 ///
250 /// Popular phrases include:
251 /// * Woah thats cheap!
252 /// * Everything used to be better in the good old days...
253 /// * Why did they move the toilet paper?
254 stream 1 mutter: String,
255 }
256 }
257
258 /// The initial request to buy tomatoes.
259 #[derive(Debug)]
260 pub struct BuyTomatoesRequest {
261 /// The tomatoes you want to buy.
262 pub amount: usize,
263 }
264
265 /// The price for something.
266 #[derive(Debug)]
267 pub struct Price {
268 /// The total price in cents.
269 pub total_price_cents: usize,
270 }
271
272 /// Payment options for purchasing tomatoes.
273 #[derive(Debug)]
274 pub enum Payment {
275 /// Payment in money.
276 Money {
277 /// The amount of money, in cents.
278 cents: usize,
279 },
280
281 /// Payment with an NFT.
282 NonFungibleToken,
283 }
284
285 /// The response of a cashier when buying tomatoes.
286 #[derive(Debug)]
287 pub enum BuyTomatoesResponse {
288 /// A final greeting and your receipt.
289 ThankYouComeAgain(Receipt),
290
291 /// Security has been called.
292 ICalledSecurity,
293 }
294
295 /// A receipt for your purchase.
296 #[derive(Debug)]
297 pub struct Receipt {
298 /// The number of tomatoes you bought.
299 pub amount_of_tomatoes: usize,
300
301 /// The total price you paid for the tomatoes.
302 pub total_price_cents: usize,
303
304 /// If the cashier really liked you, they may write their phone number on the receipt with pen.
305 pub phone_number: Option<String>,
306 }
307}