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