allframe_macros/
lib.rs

1//! AllFrame Procedural Macros
2//!
3//! This crate provides compile-time code generation for AllFrame,
4//! including dependency injection, OpenAPI schema generation, and more.
5
6#![deny(warnings)]
7
8mod api;
9mod arch;
10mod cqrs;
11mod di;
12mod error;
13mod otel;
14
15use proc_macro::TokenStream;
16
17// Note: The `provide` attribute is handled directly by `di_container` macro
18// It doesn't need a separate proc_macro_attribute since it's consumed during
19// parsing
20
21/// Compile-time dependency injection container
22///
23/// Generates a container with automatic dependency resolution at compile time.
24///
25/// # Example
26/// ```ignore
27/// #[di_container]
28/// struct AppContainer {
29///     database: DatabaseService,
30///     repository: UserRepository,
31/// }
32///
33/// let container = AppContainer::new();
34/// ```
35#[proc_macro_attribute]
36pub fn di_container(attr: TokenStream, item: TokenStream) -> TokenStream {
37    let attr = proc_macro2::TokenStream::from(attr);
38    let item = proc_macro2::TokenStream::from(item);
39
40    di::di_container_impl(attr, item)
41        .unwrap_or_else(|err| err.to_compile_error())
42        .into()
43}
44
45/// API handler with auto OpenAPI generation
46///
47/// Generates OpenAPI 3.1 schema for the annotated function.
48///
49/// # Example
50/// ```ignore
51/// #[api_handler(path = "/users", method = "POST", description = "Create user")]
52/// async fn create_user(req: CreateUserRequest) -> CreateUserResponse {
53///     // handler implementation
54/// }
55///
56/// // Generated function:
57/// // fn create_user_openapi_schema() -> String { /* JSON schema */ }
58/// ```
59#[proc_macro_attribute]
60pub fn api_handler(attr: TokenStream, item: TokenStream) -> TokenStream {
61    let attr = proc_macro2::TokenStream::from(attr);
62    let item = proc_macro2::TokenStream::from(item);
63
64    api::api_handler_impl(attr, item)
65        .unwrap_or_else(|err| err.to_compile_error())
66        .into()
67}
68
69/// Marks a type as part of the Domain layer (Layer 1)
70///
71/// Domain entities contain pure business logic with no infrastructure
72/// dependencies.
73///
74/// # Example
75/// ```ignore
76/// #[domain]
77/// struct User {
78///     id: UserId,
79///     email: Email,
80/// }
81/// ```
82#[proc_macro_attribute]
83pub fn domain(attr: TokenStream, item: TokenStream) -> TokenStream {
84    let attr = proc_macro2::TokenStream::from(attr);
85    let item = proc_macro2::TokenStream::from(item);
86
87    arch::domain_impl(attr, item)
88        .unwrap_or_else(|err| err.to_compile_error())
89        .into()
90}
91
92/// Marks a type as part of the Repository layer (Layer 2)
93///
94/// Repositories handle data access and can depend on Domain entities.
95///
96/// # Example
97/// ```ignore
98/// #[repository]
99/// trait UserRepository: Send + Sync {
100///     async fn find(&self, id: UserId) -> Option<User>;
101/// }
102/// ```
103#[proc_macro_attribute]
104pub fn repository(attr: TokenStream, item: TokenStream) -> TokenStream {
105    let attr = proc_macro2::TokenStream::from(attr);
106    let item = proc_macro2::TokenStream::from(item);
107
108    arch::repository_impl(attr, item)
109        .unwrap_or_else(|err| err.to_compile_error())
110        .into()
111}
112
113/// Marks a type as part of the Use Case layer (Layer 3)
114///
115/// Use cases orchestrate application logic and can depend on Repositories and
116/// Domain.
117///
118/// # Example
119/// ```ignore
120/// #[use_case]
121/// struct GetUserUseCase {
122///     repo: Arc<dyn UserRepository>,
123/// }
124/// ```
125#[proc_macro_attribute]
126pub fn use_case(attr: TokenStream, item: TokenStream) -> TokenStream {
127    let attr = proc_macro2::TokenStream::from(attr);
128    let item = proc_macro2::TokenStream::from(item);
129
130    arch::use_case_impl(attr, item)
131        .unwrap_or_else(|err| err.to_compile_error())
132        .into()
133}
134
135/// Marks a type as part of the Handler layer (Layer 4)
136///
137/// Handlers are entry points (HTTP/gRPC/GraphQL) and can only depend on Use
138/// Cases. Handlers CANNOT depend on Repositories directly - they must go
139/// through Use Cases.
140///
141/// # Example
142/// ```ignore
143/// #[handler]
144/// struct GetUserHandler {
145///     use_case: Arc<GetUserUseCase>,
146/// }
147/// ```
148#[proc_macro_attribute]
149pub fn handler(attr: TokenStream, item: TokenStream) -> TokenStream {
150    let attr = proc_macro2::TokenStream::from(attr);
151    let item = proc_macro2::TokenStream::from(item);
152
153    arch::handler_impl(attr, item)
154        .unwrap_or_else(|err| err.to_compile_error())
155        .into()
156}
157
158/// Marks a struct as a Command (CQRS write operation)
159///
160/// Commands represent write operations that change state and produce events.
161///
162/// # Example
163/// ```ignore
164/// #[command]
165/// struct CreateUserCommand {
166///     email: String,
167///     name: String,
168/// }
169/// ```
170#[proc_macro_attribute]
171pub fn command(attr: TokenStream, item: TokenStream) -> TokenStream {
172    let attr = proc_macro2::TokenStream::from(attr);
173    let item = proc_macro2::TokenStream::from(item);
174
175    cqrs::command_impl(attr, item)
176        .unwrap_or_else(|err| err.to_compile_error())
177        .into()
178}
179
180/// Marks a struct as a Query (CQRS read operation)
181///
182/// Queries represent read operations that don't change state.
183///
184/// # Example
185/// ```ignore
186/// #[query]
187/// struct GetUserQuery {
188///     user_id: String,
189/// }
190/// ```
191#[proc_macro_attribute]
192pub fn query(attr: TokenStream, item: TokenStream) -> TokenStream {
193    let attr = proc_macro2::TokenStream::from(attr);
194    let item = proc_macro2::TokenStream::from(item);
195
196    cqrs::query_impl(attr, item)
197        .unwrap_or_else(|err| err.to_compile_error())
198        .into()
199}
200
201/// Marks an enum or struct as an Event
202///
203/// Events represent immutable facts that have occurred in the system.
204///
205/// # Example
206/// ```ignore
207/// #[event]
208/// enum UserEvent {
209///     Created { user_id: String, email: String },
210///     Updated { user_id: String, email: String },
211/// }
212/// ```
213#[proc_macro_attribute]
214pub fn event(attr: TokenStream, item: TokenStream) -> TokenStream {
215    let attr = proc_macro2::TokenStream::from(attr);
216    let item = proc_macro2::TokenStream::from(item);
217
218    cqrs::event_impl(attr, item)
219        .unwrap_or_else(|err| err.to_compile_error())
220        .into()
221}
222
223/// Marks a function as a Command Handler
224///
225/// Command handlers process commands and produce events.
226///
227/// # Example
228/// ```ignore
229/// #[command_handler]
230/// async fn handle_create_user(cmd: CreateUserCommand) -> Result<Vec<Event>, String> {
231///     Ok(vec![Event::UserCreated { ... }])
232/// }
233/// ```
234#[proc_macro_attribute]
235pub fn command_handler(attr: TokenStream, item: TokenStream) -> TokenStream {
236    let attr = proc_macro2::TokenStream::from(attr);
237    let item = proc_macro2::TokenStream::from(item);
238
239    cqrs::command_handler_impl(attr, item)
240        .unwrap_or_else(|err| err.to_compile_error())
241        .into()
242}
243
244/// Marks a function as a Query Handler
245///
246/// Query handlers process queries and return data from projections.
247///
248/// # Example
249/// ```ignore
250/// #[query_handler]
251/// async fn handle_get_user(query: GetUserQuery) -> Result<Option<User>, String> {
252///     Ok(Some(user))
253/// }
254/// ```
255#[proc_macro_attribute]
256pub fn query_handler(attr: TokenStream, item: TokenStream) -> TokenStream {
257    let attr = proc_macro2::TokenStream::from(attr);
258    let item = proc_macro2::TokenStream::from(item);
259
260    cqrs::query_handler_impl(attr, item)
261        .unwrap_or_else(|err| err.to_compile_error())
262        .into()
263}
264
265/// Marks a function to be automatically traced with OpenTelemetry
266///
267/// Automatically creates spans with proper context propagation.
268///
269/// # Example
270/// ```ignore
271/// #[traced]
272/// async fn fetch_user(user_id: String) -> Result<User, Error> {
273///     // Span created automatically with name "fetch_user"
274///     // Span includes function arguments as attributes
275///     Ok(user)
276/// }
277/// ```
278#[proc_macro_attribute]
279pub fn traced(attr: TokenStream, item: TokenStream) -> TokenStream {
280    let attr = proc_macro2::TokenStream::from(attr);
281    let item = proc_macro2::TokenStream::from(item);
282
283    otel::traced_impl(attr, item)
284        .unwrap_or_else(|err| err.to_compile_error())
285        .into()
286}
287
288/// Derive macro for automatic gRPC status conversion
289///
290/// Generates `From<Error> for tonic::Status` implementation.
291/// Use `#[grpc(CODE)]` on variants to specify the gRPC status code.
292///
293/// # Example
294/// ```ignore
295/// use allframe_macros::GrpcError;
296/// use thiserror::Error;
297///
298/// #[derive(Error, Debug, GrpcError)]
299/// pub enum AppError {
300///     #[error("Unauthenticated: {0}")]
301///     #[grpc(UNAUTHENTICATED)]
302///     Unauthenticated(String),
303///
304///     #[error("Rate limited")]
305///     #[grpc(RESOURCE_EXHAUSTED)]
306///     RateLimited,
307///
308///     #[error("Not found: {0}")]
309///     #[grpc(NOT_FOUND)]
310///     NotFound(String),
311///
312///     #[error("Internal error: {0}")]
313///     #[grpc(INTERNAL)]
314///     Internal(String),
315/// }
316///
317/// // Auto-generates: impl From<AppError> for tonic::Status
318/// ```
319///
320/// # Supported gRPC Codes
321/// - `OK`, `CANCELLED`, `UNKNOWN`, `INVALID_ARGUMENT`
322/// - `DEADLINE_EXCEEDED`, `NOT_FOUND`, `ALREADY_EXISTS`
323/// - `PERMISSION_DENIED`, `RESOURCE_EXHAUSTED`, `FAILED_PRECONDITION`
324/// - `ABORTED`, `OUT_OF_RANGE`, `UNIMPLEMENTED`, `INTERNAL`
325/// - `UNAVAILABLE`, `DATA_LOSS`, `UNAUTHENTICATED`
326#[proc_macro_derive(GrpcError, attributes(grpc))]
327pub fn grpc_error(input: TokenStream) -> TokenStream {
328    let input = proc_macro2::TokenStream::from(input);
329
330    error::grpc_error_impl(input)
331        .unwrap_or_else(|err| err.to_compile_error())
332        .into()
333}
334
335#[cfg(test)]
336mod tests {
337    #[test]
338    fn test_macros_crate_compiles() {
339        assert!(true);
340    }
341}