1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
//! Extra macro documentation.

#[doc(hidden)]
pub use fizyr_rpc_macros::interface as interface_impl;

#[macro_export]
/// Define an RPC interface.
///
/// This macro generates an `Interface, `Client` and `Server` struct, and some more helper types.
///
/// The `Interface` struct is used to enable RPC interface introspection.
/// Currently, it only supports retrieving the documentation of the interface itself.
/// In the future, you can use this struct to get a list of services and streaming messages too.
///
/// The client struct is used to initiate requests and send stream messages.
/// It can be created from a [`PeerWriteHandle`] or a [`PeerHandle`].
/// Note that if you create the client from a [`PeerHandle`], the [`PeerReadHandle`] will not be accessible.
///
/// For the server struct it is exactly the opposite: it is used to receive requests and stream messages.
/// It can be created from a [`PeerReadHandle`] or a [`PeerHandle`],
/// but creating it from a [`PeerHandle`] will discard the [`PeerWriteHandle`].
///
/// # Example
///
/// See the [`interface_example`] module for an example, with the source code and generated documentation.
///
/// # Syntax
///
/// The syntax for the macro is as follows:
/// ```no_compile
/// fizyr_rpc::interface! {
///     // The `interface` keyword defines an RPC interface.
///     // You must have exactly one interface definition in the macro invocation.
///     //
///     // The $interface_name is used in automatically generated documentation and should be UpperCamelCase.
///     //
///     // You can adjust the visiblity of the generated items with the `pub` keyword as normal.
///     // By default, all generated items are private.
///     //
///     // Each item can have user written documentation.
///     // Simply write doc comments with triple slashes as usual.
///     // This applies to the interface definition, service definitions, update definitions and stream definitions.
///     //
///     // The documentation writter on the `interface` item can be retrieved through the introspection API,
///     // but does not appear in rustdoc.
///     pub interface $interface_name {
///         // The `service` keyword defines a service.
///         //
///         // You can have any amount of service definitions inside an interface definition.
///         //
///         // The $id is used as the service ID and must be an i32.
///         // The ID must be unique for all services in the interface.
///         //
///         // The $name is the name of the service.
///         // It is used to generate function and type names.
///         // It must be a valid Rust identifier and should be lowercase with underscores.
///         //
///         // The $request_type and $response_type indicate the message body for the request and the response.
///         // If there is no data in a request or response, you can use the unit type: `()`
///         //
///         // If the service has no update messages, you can end the definition with a comma.
///         // See the next item for the syntax of services with update messages.
///         service $id $name: $request_type -> $response_type,
///
///         // If a service has update messages, you can declare them in the service block.
///         service $id $name: $request_type -> $response_type {
///             // The `request_update` keyword defines a request update.
///             // You can have any amount of request updates inside a service definition.
///             //
///             // The $id is used as the service ID for the update and must be an i32.
///             // The ID must be unique for all request updates in the service.
///             //
///             // The $name is the name of the update message.
///             // It is used to generate function and type names.
///             // It must be a valid Rust identifier and should be lowercase with underscores.
///             //
///             // The $body_type indicates the type of the message.
///             // If there is no data in the message, you can use the unit type: `()`
///             request_update $id $name: $body_type,
///
///             // The `response_update` keyword defines a response update.
///             // You can have any amount of response updates inside a service definition.
///             //
///             // The $id is used as the service ID for the update and must be an i32.
///             // The ID must be unique for all response updates in the service.
///             //
///             // The $name is the name of the update message.
///             // It is used to generate function and type names.
///             // It must be a valid Rust identifier and should be lowercase with underscores.
///             //
///             // The $body_type indicates the type of the message.
///             // If there is no data in the message, you can use the unit type: `()`
///             response_update $id $name: $body_type,
///         }
///
///         // The `stream` keyword defines a stream message.
///         // You can have any amount of stream definitions in an interface definition.
///         //
///         // The $id is used as the service ID of the stream message and must be an i32.
///         // The ID must be unique for all streams in the interface.
///         //
///         // The $name is the name of the stream.
///         // It is used to generate function and type names.
///         // It must be a valid Rust identifier and should be lowercase with underscores.
///         //
///         // The $body_type indicates the type of the message.
///         // If there is no data in the message, you can use the unit type: `()`
///        stream $id $name: $body_type,
///     }
/// }
/// ```
///
/// [`PeerHandle`]: crate::PeerHandle
/// [`PeerWriteHandle`]: crate::PeerWriteHandle
/// [`PeerReadHandle`]: crate::PeerReadHandle
macro_rules! interface {
	($($tokens:tt)*) => {
		$crate::macros::interface_impl!{$crate; $($tokens)*}
	}
}

/// Example module for the `interface!` macro.
///
/// You can compare the source of the generated documentation to inspect the generated API.
/// The most important generated types are [`Client`] and [`Server`].
///
/// [`Client`]: interface_example::Client
/// [`Server`]: interface_example::Server
///
/// ```no_compile
/// fizyr_rpc::interface! {
///     /// RPC interface for the supermarket.
///     pub interface Supermarket {
///         /// Greet the cashier.
///         ///
///         /// The cashier will reply with their own greeting.
///         service 1 greet_cashier: String -> String,
///
///         /// Purchase tomatoes.
///         ///
///         /// The response of the cashier depends on the update messages exchanged.
///         /// If you run away after they have sent the `price`, or if you pay with a nun-fungable token,
///         /// they will respond with [`BuyTomatoesResponse::ICalledSecurity`].
///         ///
///         /// If you pay with the correct amount, they will respond with [`BuyTomatoesResponse::ThankYouComeAgain`].
///         service 2 buy_tomatoes: BuyTomatoesRequest -> BuyTomatoesResponse {
///             /// Sent once by the cashier to notify you of the price of the tomatoes.
///             response_update 1 price: Price,
///
///             /// Sent by the client to pay for the tomatoes.
///             request_update 1 pay: Payment,
///
///             /// Sent by broke or kleptomanic clients that still want tomatoes.
///             request_update 2 run_away: (),
///         }
///
///         /// Mutter something as you're walking through the supermarket.
///         ///
///         /// Since no-one will respond, this is a stream rather than a service.
///         ///
///         /// Popular phrases include:
///         ///  * Woah thats cheap!
///         ///  * Everything used to be better in the good old days...
///         ///  * Why did they move the toilet paper?
///         stream 1 mutter: String,
///     }
/// }
///
/// /// The initial request to buy tomatoes.
/// #[derive(Debug)]
/// pub struct BuyTomatoesRequest {
///     /// The tomatoes you want to buy.
///     pub amount: usize,
/// }
///
/// /// The price for something.
/// #[derive(Debug)]
/// pub struct Price {
///     /// The total price in cents.
///     pub total_price_cents: usize,
/// }
///
/// /// Payment options for purchasing tomatoes.
/// #[derive(Debug)]
/// pub enum Payment {
///     /// Payment in money.
///     Money {
///         /// The amount of money in cents.
///         cents: usize
///     },
///
///     /// Payment with an NFT.
///     NonFungibleToken,
/// }
///
/// /// The response of a cashier when buying tomatoes.
/// #[derive(Debug)]
/// pub enum BuyTomatoesResponse {
///     /// A final greeting and your receipt.
///     ThankYouComeAgain(Receipt),
///
///     /// Security has been called.
///     ICalledSecurity,
/// }
///
/// /// A receipt for your purchase.
/// #[derive(Debug)]
/// pub struct Receipt {
///     /// The number of tomatoes you bought.
///     pub amount_of_tomatoes: usize,
///
///     /// The total price you paid for the tomatoes.
///     pub total_price_cents: usize,
///
///     /// If the cashier really liked you, they may write their phone number on the receipt with pen.
///     pub phone_number: Option<String>,
/// }
/// ```
pub mod interface_example {
	interface! {
		/// RPC interface for the supermarket.
		pub interface Supermarket {
			/// Greet the cashier.
			///
			/// The cashier will reply with their own greeting.
			service 1 greet_cashier: String -> String,

			/// Purchase tomatoes.
			///
			/// The response of the cashier depends on the update messages exchanged.
			/// If you run away after they have sent the `price`, or if you pay with a nun-fungable token,
			/// they will respond with [`BuyTomatoesResponse::ICalledSecurity`].
			///
			/// If you pay with the correct amount, they will respond with [`BuyTomatoesResponse::ThankYouComeAgain`].
			service 2 buy_tomatoes: BuyTomatoesRequest -> BuyTomatoesResponse {
				/// Sent once by the cashier to notify you of the price of the tomatoes.
				response_update 1 price: Price,

				/// Sent by the client to pay for the tomatoes.
				request_update 1 pay: Payment,

				/// Sent by broke or kleptomanic clients that still want tomatoes.
				request_update 2 run_away: (),
			}

			/// Mutter something as you're walking through the supermarket.
			///
			/// Since no-one will respond, this is a stream rather than a service.
			///
			/// Popular phrases include:
			///  * Woah thats cheap!
			///  * Everything used to be better in the good old days...
			///  * Why did they move the toilet paper?
			stream 1 mutter: String,
		}
	}

	/// The initial request to buy tomatoes.
	#[derive(Debug)]
	pub struct BuyTomatoesRequest {
		/// The tomatoes you want to buy.
		pub amount: usize,
	}

	/// The price for something.
	#[derive(Debug)]
	pub struct Price {
		/// The total price in cents.
		pub total_price_cents: usize,
	}

	/// Payment options for purchasing tomatoes.
	#[derive(Debug)]
	pub enum Payment {
		/// Payment in money.
		Money {
			/// The amount of money, in cents.
			cents: usize,
		},

		/// Payment with an NFT.
		NonFungibleToken,
	}

	/// The response of a cashier when buying tomatoes.
	#[derive(Debug)]
	pub enum BuyTomatoesResponse {
		/// A final greeting and your receipt.
		ThankYouComeAgain(Receipt),

		/// Security has been called.
		ICalledSecurity,
	}

	/// A receipt for your purchase.
	#[derive(Debug)]
	pub struct Receipt {
		/// The number of tomatoes you bought.
		pub amount_of_tomatoes: usize,

		/// The total price you paid for the tomatoes.
		pub total_price_cents: usize,

		/// If the cashier really liked you, they may write their phone number on the receipt with pen.
		pub phone_number: Option<String>,
	}
}