occams_rpc_api_macros/lib.rs
1extern crate proc_macro;
2use proc_macro::TokenStream;
3
4mod endpoint_async;
5mod service;
6mod service_mux_struct;
7
8/// The `#[service]` macro is applied to an `impl` block to automatically generate the `ServiceTrait` implementation for the type.
9///
10/// - When applied to an inherent `impl` block (without `impl Trait`), methods intended as service methods should be marked with `#[method]`.
11/// - When applied to a trait `impl` block, all methods defined in the trait will be registered as service methods.
12/// - All service methods must return `Result<T, RpcError<E>>`, where `E` is a user-defined error type that implements `RpcErrCodec`.
13///
14/// The service method recognizes:
15/// - `fn` (which is considered non-blocking)
16/// - `async fn`
17/// - `impl Future`
18/// - trait methods wrapped by `async_trait`
19///
20/// # Usage
21///
22/// Without `impl Trait` (inherent implementation):
23///
24/// ```rust
25/// use razor_rpc::server::{service, method};
26/// use razor_rpc::error::RpcError;
27/// use serde::{Deserialize, Serialize};
28/// use razor_rpc::server::ServiceStatic;
29/// use razor_rpc_codec::MsgpCodec;
30///
31/// #[derive(Debug, Deserialize, Serialize)]
32/// pub struct AddArgs {
33/// a: i32,
34/// b: i32,
35/// }
36///
37/// #[derive(Debug, Deserialize, Serialize)]
38/// pub struct AddResp {
39/// result: i32,
40/// }
41///
42/// pub struct CalculatorService;
43///
44/// #[service]
45/// impl CalculatorService {
46/// #[method]
47/// async fn add(&self, args: AddArgs) -> Result<AddResp, RpcError<()>> {
48/// Ok(AddResp { result: args.a + args.b })
49/// }
50/// }
51///
52/// // This will generate a ServiceStatic implementation that can be used as:
53/// // let service = CalculatorService;
54/// // service.serve(request).await;
55/// ```
56///
57/// With trait implementation using `impl Future`:
58///
59/// ```rust
60/// use razor_rpc::server::service;
61/// use razor_rpc::error::RpcError;
62/// use serde::{Deserialize, Serialize};
63/// use std::future::Future;
64///
65/// #[derive(Debug, Deserialize, Serialize)]
66/// pub struct EchoArgs {
67/// message: String,
68/// }
69///
70/// #[derive(Debug, Deserialize, Serialize)]
71/// pub struct EchoResp {
72/// echoed: String,
73/// }
74///
75/// pub trait EchoService: Send + Sync {
76/// fn echo(&self, args: EchoArgs) -> impl Future<Output = Result<EchoResp, RpcError<()>>> + Send;
77/// }
78///
79/// pub struct EchoServiceImpl;
80///
81/// #[service]
82/// impl EchoService for EchoServiceImpl {
83/// fn echo(&self, args: EchoArgs) -> impl Future<Output = Result<EchoResp, RpcError<()>>> + Send {
84/// async move {
85/// Ok(EchoResp { echoed: args.message })
86/// }
87/// }
88/// }
89/// ```
90#[proc_macro_attribute]
91pub fn service(_attr: TokenStream, item: TokenStream) -> TokenStream {
92 service::service(_attr, item)
93}
94
95/// A marker attribute for methods in an inherent `impl` block that should be exposed as RPC methods.
96/// This is not needed when using a trait-based implementation.
97///
98/// Refer to document of attr macro `#[service]`
99#[proc_macro_attribute]
100pub fn method(_attr: TokenStream, item: TokenStream) -> TokenStream {
101 item
102}
103
104/// The `#[service_mux_struct]` macro is applied to a **struct** to implement `ServiceTrait` on it.
105/// It acts as a dispatcher, routing `serve()` calls to the correct service based on the `req.service` field.
106///
107/// Each field in the struct must hold a service that implements `ServiceTrait` (e.g., wrapped in an `Arc`).
108/// The macro generates a `serve` implementation that matches `req.service` against the field names of the struct.
109///
110/// # Usage
111///
112/// ```rust
113/// use razor_rpc::server::{service, service_mux_struct, method};
114/// use razor_rpc::error::RpcError;
115/// use serde::{Deserialize, Serialize};
116/// use std::sync::Arc;
117///
118/// // Define request/response types
119/// #[derive(Debug, Deserialize, Serialize)]
120/// pub struct MathArgs {
121/// value: i32,
122/// }
123///
124/// #[derive(Debug, Deserialize, Serialize)]
125/// pub struct MathResp {
126/// result: i32,
127/// }
128///
129/// // Define services
130/// pub struct AddService;
131/// #[service]
132/// impl AddService {
133/// #[method]
134/// async fn add(&self, args: MathArgs) -> Result<MathResp, RpcError<()>> {
135/// Ok(MathResp { result: args.value + 1 })
136/// }
137/// }
138///
139/// pub struct MultiplyService;
140/// #[service]
141/// impl MultiplyService {
142/// #[method]
143/// async fn multiply(&self, args: MathArgs) -> Result<MathResp, RpcError<()>> {
144/// Ok(MathResp { result: args.value * 2 })
145/// }
146/// }
147///
148/// // Create a service multiplexer
149/// #[service_mux_struct]
150/// pub struct ServiceMux {
151/// pub add: Arc<AddService>,
152/// pub multiply: Arc<MultiplyService>,
153/// }
154///
155/// // Usage:
156/// // let mux = ServiceMux {
157/// // add: Arc::new(AddService),
158/// // multiply: Arc::new(MultiplyService),
159/// // };
160/// ```
161#[proc_macro_attribute]
162pub fn service_mux_struct(_attr: TokenStream, item: TokenStream) -> TokenStream {
163 service_mux_struct::service_mux_struct(_attr, item)
164}
165
166/// The `#[endpoint_async]` macro applies to a trait to generate a client for remote api calls
167/// for async context.
168///
169/// NOTE: the generated struct and interface will appear in your `cargo doc`.
170///
171/// Rules:
172/// - trait can be wrapped async_trait, optionally.
173/// - If trait is not async_trait, then the method should use the signature `impl Future + Send`
174/// - No fn is allowed.
175/// - All method should have one and only argument, and return type should be `Result<Resp, RpcError<E>>`, where `E: RpcErrCodec`
176///
177/// # Usage
178///
179/// Define a service trait with the `#[endpoint_async]` attribute:
180///
181/// ```rust
182/// use razor_rpc::client::endpoint_async;
183/// use razor_rpc::error::RpcError;
184/// use serde::{Deserialize, Serialize};
185/// use std::future::Future;
186///
187/// #[derive(Debug, Deserialize, Serialize)]
188/// pub struct AddArgs {
189/// pub a: i32,
190/// pub b: i32,
191/// }
192///
193/// #[derive(Debug, Deserialize, Serialize, Default)]
194/// pub struct AddResp {
195/// pub result: i32,
196/// }
197///
198/// #[endpoint_async(CalculatorClient)]
199/// pub trait CalculatorService {
200/// fn add(&self, args: AddArgs) -> impl Future<Output = Result<AddResp, RpcError<()>>> + Send;
201/// }
202///
203/// // This generates a CalculatorClient struct that can be used with a client caller:
204/// // let client = CalculatorClient::new(caller);
205/// // let result = client.add(AddArgs { a: 1, b: 2 }).await;
206/// ```
207///
208/// The macro also supports `async fn` methods when used with `#[async_trait]` (though this is not recommended per the project guidelines).
209#[proc_macro_attribute]
210pub fn endpoint_async(attr: TokenStream, item: TokenStream) -> TokenStream {
211 endpoint_async::endpoint_async(attr, item)
212}