ash_rpc_core/macros.rs
1//! Convenience macros for JSON-RPC response creation.
2
3/// Create a success response with a result value and optional ID
4///
5/// # Examples:
6/// ```ignore
7/// // Success with ID
8/// rpc_success!(42, Some(1))
9///
10/// // Success with ID from variable
11/// rpc_success!(result, id)
12///
13/// // Success without ID
14/// rpc_success!({"status": "ok"})
15/// ```
16#[macro_export]
17macro_rules! rpc_success {
18 ($result:expr, $id:expr) => {
19 $crate::ResponseBuilder::new()
20 .success(serde_json::json!($result))
21 .id($id)
22 .build()
23 };
24 ($result:expr) => {
25 $crate::ResponseBuilder::new()
26 .success(serde_json::json!($result))
27 .id(None)
28 .build()
29 };
30}
31
32/// Create an error response with code, message, and optional ID
33///
34/// # Usage:
35/// ```ignore
36/// // Error with explicit code, message and ID
37/// rpc_error!(-32602, "Invalid parameters", Some(1))
38///
39/// // Error with code and message, ID from variable
40/// rpc_error!(-32602, "Invalid parameters", id)
41///
42/// // Error without ID
43/// rpc_error!(-32601, "Method not found")
44///
45/// // Error using predefined error codes
46/// rpc_error!(error_codes::INVALID_PARAMS, "Invalid parameters", id)
47/// ```
48#[macro_export]
49macro_rules! rpc_error {
50 ($code:expr, $message:expr, $id:expr) => {
51 $crate::ResponseBuilder::new()
52 .error($crate::ErrorBuilder::new($code, $message).build())
53 .id($id)
54 .build()
55 };
56 ($code:expr, $message:expr) => {
57 $crate::ResponseBuilder::new()
58 .error($crate::ErrorBuilder::new($code, $message).build())
59 .id(None)
60 .build()
61 };
62}
63
64/// Create an error response with code, message, additional data and optional ID
65///
66/// # Usage:
67/// ```ignore
68/// // Error with data
69/// rpc_error_with_data!(-32602, "Invalid parameters", {"expected": "array"}, Some(1))
70///
71/// // Error with data but no ID
72/// rpc_error_with_data!(-32602, "Invalid parameters", {"expected": "array"})
73/// ```
74#[macro_export]
75macro_rules! rpc_error_with_data {
76 ($code:expr, $message:expr, $data:expr, $id:expr) => {
77 $crate::ResponseBuilder::new()
78 .error(
79 $crate::ErrorBuilder::new($code, $message)
80 .data(serde_json::json!($data))
81 .build(),
82 )
83 .id($id)
84 .build()
85 };
86 ($code:expr, $message:expr, $data:expr) => {
87 $crate::ResponseBuilder::new()
88 .error(
89 $crate::ErrorBuilder::new($code, $message)
90 .data(serde_json::json!($data))
91 .build(),
92 )
93 .id(None)
94 .build()
95 };
96}
97
98/// Common error response shortcuts using predefined error codes
99///
100/// # Usage:
101/// ```ignore
102/// rpc_invalid_params!("Expected array of two numbers", id)
103/// rpc_method_not_found!(id)
104/// rpc_parse_error!("Invalid JSON", id)
105/// rpc_internal_error!("Database connection failed", id)
106/// ```
107#[macro_export]
108macro_rules! rpc_invalid_params {
109 ($message:expr, $id:expr) => {
110 rpc_error!($crate::error_codes::INVALID_PARAMS, $message, $id)
111 };
112 ($message:expr) => {
113 rpc_error!($crate::error_codes::INVALID_PARAMS, $message)
114 };
115}
116
117#[macro_export]
118macro_rules! rpc_method_not_found {
119 ($id:expr) => {
120 rpc_error!(
121 $crate::error_codes::METHOD_NOT_FOUND,
122 "Method not found",
123 $id
124 )
125 };
126 () => {
127 rpc_error!($crate::error_codes::METHOD_NOT_FOUND, "Method not found")
128 };
129}
130
131#[macro_export]
132macro_rules! rpc_parse_error {
133 ($message:expr, $id:expr) => {
134 rpc_error!($crate::error_codes::PARSE_ERROR, $message, $id)
135 };
136 ($message:expr) => {
137 rpc_error!($crate::error_codes::PARSE_ERROR, $message)
138 };
139}
140
141#[macro_export]
142macro_rules! rpc_internal_error {
143 ($message:expr, $id:expr) => {
144 rpc_error!($crate::error_codes::INTERNAL_ERROR, $message, $id)
145 };
146 ($message:expr) => {
147 rpc_error!($crate::error_codes::INTERNAL_ERROR, $message)
148 };
149}
150
151/// Create a JSON-RPC request
152///
153/// # Usage:
154/// ```ignore
155/// // Request with method, params and ID
156/// rpc_request!("add", [5, 3], 1)
157///
158/// // Request with method and ID (no params)
159/// rpc_request!("ping", 2)
160///
161/// // Request with method only (notification - no ID)
162/// rpc_request!("log")
163/// ```
164#[macro_export]
165macro_rules! rpc_request {
166 ($method:expr, $params:expr, $id:expr) => {
167 $crate::RequestBuilder::new($method)
168 .params(serde_json::json!($params))
169 .id(serde_json::json!($id))
170 .build()
171 };
172 ($method:expr, $id:expr) => {
173 $crate::RequestBuilder::new($method)
174 .id(serde_json::json!($id))
175 .build()
176 };
177 ($method:expr) => {
178 $crate::RequestBuilder::new($method).build()
179 };
180}
181
182/// Create a JSON-RPC notification
183///
184/// # Usage:
185/// ```ignore
186/// // Notification with method and params
187/// rpc_notification!("log", {"level": "info", "message": "Hello"})
188///
189/// // Notification with method only
190/// rpc_notification!("ping")
191/// ```
192#[macro_export]
193macro_rules! rpc_notification {
194 ($method:expr, $params:expr) => {
195 $crate::NotificationBuilder::new($method)
196 .params(serde_json::json!($params))
197 .build()
198 };
199 ($method:expr) => {
200 $crate::NotificationBuilder::new($method).build()
201 };
202}
203
204/// Create a JSON-RPC error object (not a response)
205///
206/// # Usage:
207/// ```ignore
208/// // Error with code and message
209/// rpc_error_obj!(-32602, "Invalid parameters")
210///
211/// // Error with code, message and data
212/// rpc_error_obj!(-32602, "Invalid parameters", {"expected": "array"})
213/// ```
214#[macro_export]
215macro_rules! rpc_error_obj {
216 ($code:expr, $message:expr, $data:expr) => {
217 $crate::ErrorBuilder::new($code, $message)
218 .data(serde_json::json!($data))
219 .build()
220 };
221 ($code:expr, $message:expr) => {
222 $crate::ErrorBuilder::new($code, $message).build()
223 };
224}
225
226//
227// Transport Macros - Easy server creation
228//
229
230/// Create and run a TCP JSON-RPC server
231///
232/// # Usage:
233/// ```ignore
234/// // Basic TCP server with registry
235/// rpc_tcp_server!("127.0.0.1:8080", registry);
236///
237/// // TCP server with error handling
238/// rpc_tcp_server!("127.0.0.1:8080", registry).expect("Failed to start server");
239/// ```
240#[cfg(feature = "tcp")]
241#[macro_export]
242macro_rules! rpc_tcp_server {
243 ($addr:expr, $processor:expr) => {{
244 let server = $crate::transport::tcp::TcpServer::builder($addr)
245 .processor($processor)
246 .build()?;
247 server.run()
248 }};
249}
250
251/// Create and run a TCP streaming JSON-RPC server
252///
253/// # Usage:
254/// ```ignore
255/// // Basic TCP streaming server
256/// rpc_tcp_stream_server!("127.0.0.1:8080", registry).await?;
257///
258/// // With error handling
259/// rpc_tcp_stream_server!("127.0.0.1:8080", registry).await.expect("Server failed");
260/// ```
261#[cfg(feature = "tcp-stream")]
262#[macro_export]
263macro_rules! rpc_tcp_stream_server {
264 ($addr:expr, $processor:expr) => {
265 async move {
266 let server = $crate::transport::tcp_stream::TcpStreamServer::builder($addr)
267 .processor($processor)
268 .build()?;
269 server.run().await
270 }
271 };
272}
273
274/// Create a TCP streaming JSON-RPC client
275///
276/// # Usage:
277/// ```ignore
278/// // Create and connect client
279/// let mut client = rpc_tcp_stream_client!("127.0.0.1:8080").await?;
280///
281/// // Send request and get response
282/// let response = client.call("method_name", Some(params)).await?;
283/// ```
284#[cfg(feature = "tcp-stream")]
285#[macro_export]
286macro_rules! rpc_tcp_stream_client {
287 ($addr:expr) => {{
288 $crate::transport::tcp_stream::TcpStreamClient::builder($addr)
289 .build()
290 .connect()
291 }};
292}
293
294/// Create an Axum router with JSON-RPC endpoint
295///
296/// # Usage:
297/// ```ignore
298/// // Basic router with default /rpc path
299/// let app = rpc_axum_router!(registry);
300///
301/// // Router with custom path
302/// let app = rpc_axum_router!(registry, "/api/rpc");
303///
304/// // Router with additional routes
305/// let app = rpc_axum_router!(registry, "/rpc")
306/// .route("/health", get(health_check));
307/// ```
308#[cfg(feature = "axum")]
309#[macro_export]
310macro_rules! rpc_axum_router {
311 ($processor:expr, $path:expr) => {
312 $crate::transport::axum::create_rpc_router($processor, $path)
313 };
314 ($processor:expr) => {
315 $crate::transport::axum::create_rpc_router($processor, "/rpc")
316 };
317}
318
319/// Create and run an Axum server with JSON-RPC
320///
321/// # Usage:
322/// ```ignore
323/// // Basic server on default port
324/// rpc_axum_server!("127.0.0.1:3000", registry).await?;
325///
326/// // Server with custom RPC path
327/// rpc_axum_server!("127.0.0.1:3000", registry, "/api/rpc").await?;
328/// ```
329#[cfg(feature = "axum")]
330#[macro_export]
331macro_rules! rpc_axum_server {
332 ($addr:expr, $processor:expr, $path:expr) => {{
333 let app = rpc_axum_router!($processor, $path);
334 async move {
335 let listener = tokio::net::TcpListener::bind($addr).await?;
336 axum::serve(listener, app).await
337 }
338 }};
339 ($addr:expr, $processor:expr) => {{
340 let app = rpc_axum_router!($processor);
341 async move {
342 let listener = tokio::net::TcpListener::bind($addr).await?;
343 axum::serve(listener, app).await
344 }
345 }};
346}
347
348/// Create an Axum RPC layer for middleware use
349///
350/// # Usage:
351/// ```ignore
352/// // Create RPC layer
353/// let rpc_layer = rpc_axum_layer!(registry);
354///
355/// // Use in router
356/// let app = Router::new()
357/// .route("/health", get(health_check))
358/// .layer(rpc_layer);
359/// ```
360#[cfg(feature = "axum")]
361#[macro_export]
362macro_rules! rpc_axum_layer {
363 ($processor:expr, $path:expr) => {
364 $crate::transport::axum::AxumRpcLayer::builder()
365 .processor($processor)
366 .path($path)
367 .build()
368 .expect("Failed to build Axum RPC layer")
369 };
370 ($processor:expr) => {
371 $crate::transport::axum::AxumRpcLayer::builder()
372 .processor($processor)
373 .build()
374 .expect("Failed to build Axum RPC layer")
375 };
376}