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}