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;
15mod resilience;
16mod security;
17
18use proc_macro::TokenStream;
19
20// Note: The `provide` attribute is handled directly by `di_container` macro
21// It doesn't need a separate proc_macro_attribute since it's consumed during
22// parsing
23
24/// Compile-time dependency injection container
25///
26/// Generates a container with automatic dependency resolution at compile time.
27///
28/// # Attributes
29///
30/// - `#[provide(expr)]` - Use custom expression for initialization
31/// - `#[provide(from_env)]` - Load from environment using `FromEnv` trait
32/// - `#[provide(singleton)]` - Shared instance (default)
33/// - `#[provide(transient)]` - New instance on each access
34/// - `#[provide(async)]` - Async initialization using `AsyncInit` trait
35/// - `#[depends(field1, field2)]` - Explicit dependencies
36///
37/// # Example (Sync)
38/// ```ignore
39/// #[di_container]
40/// struct AppContainer {
41/// database: DatabaseService,
42/// repository: UserRepository,
43/// }
44///
45/// let container = AppContainer::new();
46/// ```
47///
48/// # Example (Async)
49/// ```ignore
50/// #[di_container]
51/// struct AppContainer {
52/// #[provide(from_env)]
53/// config: Config,
54///
55/// #[provide(singleton, async)]
56/// #[depends(config)]
57/// database: DatabasePool,
58///
59/// #[provide(transient)]
60/// service: MyService,
61/// }
62///
63/// let container = AppContainer::build().await?;
64/// ```
65#[proc_macro_attribute]
66pub fn di_container(attr: TokenStream, item: TokenStream) -> TokenStream {
67 let attr = proc_macro2::TokenStream::from(attr);
68 let item = proc_macro2::TokenStream::from(item);
69
70 di::di_container_impl(attr, item)
71 .unwrap_or_else(|err| err.to_compile_error())
72 .into()
73}
74
75/// API handler with auto OpenAPI generation
76///
77/// Generates OpenAPI 3.1 schema for the annotated function.
78///
79/// # Example
80/// ```ignore
81/// #[api_handler(path = "/users", method = "POST", description = "Create user")]
82/// async fn create_user(req: CreateUserRequest) -> CreateUserResponse {
83/// // handler implementation
84/// }
85///
86/// // Generated function:
87/// // fn create_user_openapi_schema() -> String { /* JSON schema */ }
88/// ```
89#[proc_macro_attribute]
90pub fn api_handler(attr: TokenStream, item: TokenStream) -> TokenStream {
91 let attr = proc_macro2::TokenStream::from(attr);
92 let item = proc_macro2::TokenStream::from(item);
93
94 api::api_handler_impl(attr, item)
95 .unwrap_or_else(|err| err.to_compile_error())
96 .into()
97}
98
99/// Marks a type as part of the Domain layer (Layer 1)
100///
101/// Domain entities contain pure business logic with no infrastructure
102/// dependencies.
103///
104/// # Example
105/// ```ignore
106/// #[domain]
107/// struct User {
108/// id: UserId,
109/// email: Email,
110/// }
111/// ```
112#[proc_macro_attribute]
113pub fn domain(attr: TokenStream, item: TokenStream) -> TokenStream {
114 let attr = proc_macro2::TokenStream::from(attr);
115 let item = proc_macro2::TokenStream::from(item);
116
117 arch::domain_impl(attr, item)
118 .unwrap_or_else(|err| err.to_compile_error())
119 .into()
120}
121
122/// Marks a type as part of the Repository layer (Layer 2)
123///
124/// Repositories handle data access and can depend on Domain entities.
125///
126/// # Example
127/// ```ignore
128/// #[repository]
129/// trait UserRepository: Send + Sync {
130/// async fn find(&self, id: UserId) -> Option<User>;
131/// }
132/// ```
133#[proc_macro_attribute]
134pub fn repository(attr: TokenStream, item: TokenStream) -> TokenStream {
135 let attr = proc_macro2::TokenStream::from(attr);
136 let item = proc_macro2::TokenStream::from(item);
137
138 arch::repository_impl(attr, item)
139 .unwrap_or_else(|err| err.to_compile_error())
140 .into()
141}
142
143/// Marks a type as part of the Use Case layer (Layer 3)
144///
145/// Use cases orchestrate application logic and can depend on Repositories and
146/// Domain.
147///
148/// # Example
149/// ```ignore
150/// #[use_case]
151/// struct GetUserUseCase {
152/// repo: Arc<dyn UserRepository>,
153/// }
154/// ```
155#[proc_macro_attribute]
156pub fn use_case(attr: TokenStream, item: TokenStream) -> TokenStream {
157 let attr = proc_macro2::TokenStream::from(attr);
158 let item = proc_macro2::TokenStream::from(item);
159
160 arch::use_case_impl(attr, item)
161 .unwrap_or_else(|err| err.to_compile_error())
162 .into()
163}
164
165/// Marks a type as part of the Handler layer (Layer 4)
166///
167/// Handlers are entry points (HTTP/gRPC/GraphQL) and can only depend on Use
168/// Cases. Handlers CANNOT depend on Repositories directly - they must go
169/// through Use Cases.
170///
171/// # Example
172/// ```ignore
173/// #[handler]
174/// struct GetUserHandler {
175/// use_case: Arc<GetUserUseCase>,
176/// }
177/// ```
178#[proc_macro_attribute]
179pub fn handler(attr: TokenStream, item: TokenStream) -> TokenStream {
180 let attr = proc_macro2::TokenStream::from(attr);
181 let item = proc_macro2::TokenStream::from(item);
182
183 arch::handler_impl(attr, item)
184 .unwrap_or_else(|err| err.to_compile_error())
185 .into()
186}
187
188/// Marks a struct as a Command (CQRS write operation)
189///
190/// Commands represent write operations that change state and produce events.
191///
192/// # Example
193/// ```ignore
194/// #[command]
195/// struct CreateUserCommand {
196/// email: String,
197/// name: String,
198/// }
199/// ```
200#[proc_macro_attribute]
201pub fn command(attr: TokenStream, item: TokenStream) -> TokenStream {
202 let attr = proc_macro2::TokenStream::from(attr);
203 let item = proc_macro2::TokenStream::from(item);
204
205 cqrs::command_impl(attr, item)
206 .unwrap_or_else(|err| err.to_compile_error())
207 .into()
208}
209
210/// Marks a struct as a Query (CQRS read operation)
211///
212/// Queries represent read operations that don't change state.
213///
214/// # Example
215/// ```ignore
216/// #[query]
217/// struct GetUserQuery {
218/// user_id: String,
219/// }
220/// ```
221#[proc_macro_attribute]
222pub fn query(attr: TokenStream, item: TokenStream) -> TokenStream {
223 let attr = proc_macro2::TokenStream::from(attr);
224 let item = proc_macro2::TokenStream::from(item);
225
226 cqrs::query_impl(attr, item)
227 .unwrap_or_else(|err| err.to_compile_error())
228 .into()
229}
230
231/// Marks an enum or struct as an Event
232///
233/// Events represent immutable facts that have occurred in the system.
234///
235/// # Example
236/// ```ignore
237/// #[event]
238/// enum UserEvent {
239/// Created { user_id: String, email: String },
240/// Updated { user_id: String, email: String },
241/// }
242/// ```
243#[proc_macro_attribute]
244pub fn event(attr: TokenStream, item: TokenStream) -> TokenStream {
245 let attr = proc_macro2::TokenStream::from(attr);
246 let item = proc_macro2::TokenStream::from(item);
247
248 cqrs::event_impl(attr, item)
249 .unwrap_or_else(|err| err.to_compile_error())
250 .into()
251}
252
253/// Marks a function as a Command Handler
254///
255/// Command handlers process commands and produce events.
256///
257/// # Example
258/// ```ignore
259/// #[command_handler]
260/// async fn handle_create_user(cmd: CreateUserCommand) -> Result<Vec<Event>, String> {
261/// Ok(vec![Event::UserCreated { ... }])
262/// }
263/// ```
264#[proc_macro_attribute]
265pub fn command_handler(attr: TokenStream, item: TokenStream) -> TokenStream {
266 let attr = proc_macro2::TokenStream::from(attr);
267 let item = proc_macro2::TokenStream::from(item);
268
269 cqrs::command_handler_impl(attr, item)
270 .unwrap_or_else(|err| err.to_compile_error())
271 .into()
272}
273
274/// Marks a function as a Query Handler
275///
276/// Query handlers process queries and return data from projections.
277///
278/// # Example
279/// ```ignore
280/// #[query_handler]
281/// async fn handle_get_user(query: GetUserQuery) -> Result<Option<User>, String> {
282/// Ok(Some(user))
283/// }
284/// ```
285#[proc_macro_attribute]
286pub fn query_handler(attr: TokenStream, item: TokenStream) -> TokenStream {
287 let attr = proc_macro2::TokenStream::from(attr);
288 let item = proc_macro2::TokenStream::from(item);
289
290 cqrs::query_handler_impl(attr, item)
291 .unwrap_or_else(|err| err.to_compile_error())
292 .into()
293}
294
295/// Marks a function to be automatically traced with OpenTelemetry
296///
297/// Automatically creates spans with proper context propagation.
298///
299/// # Example
300/// ```ignore
301/// #[traced]
302/// async fn fetch_user(user_id: String) -> Result<User, Error> {
303/// // Span created automatically with name "fetch_user"
304/// // Span includes function arguments as attributes
305/// Ok(user)
306/// }
307/// ```
308#[proc_macro_attribute]
309pub fn traced(attr: TokenStream, item: TokenStream) -> TokenStream {
310 let attr = proc_macro2::TokenStream::from(attr);
311 let item = proc_macro2::TokenStream::from(item);
312
313 otel::traced_impl(attr, item)
314 .unwrap_or_else(|err| err.to_compile_error())
315 .into()
316}
317
318/// Derive macro for automatic gRPC status conversion
319///
320/// Generates `From<Error> for tonic::Status` implementation.
321/// Use `#[grpc(CODE)]` on variants to specify the gRPC status code.
322///
323/// # Example
324/// ```ignore
325/// use allframe_macros::GrpcError;
326/// use thiserror::Error;
327///
328/// #[derive(Error, Debug, GrpcError)]
329/// pub enum AppError {
330/// #[error("Unauthenticated: {0}")]
331/// #[grpc(UNAUTHENTICATED)]
332/// Unauthenticated(String),
333///
334/// #[error("Rate limited")]
335/// #[grpc(RESOURCE_EXHAUSTED)]
336/// RateLimited,
337///
338/// #[error("Not found: {0}")]
339/// #[grpc(NOT_FOUND)]
340/// NotFound(String),
341///
342/// #[error("Internal error: {0}")]
343/// #[grpc(INTERNAL)]
344/// Internal(String),
345/// }
346///
347/// // Auto-generates: impl From<AppError> for tonic::Status
348/// ```
349///
350/// # Supported gRPC Codes
351/// - `OK`, `CANCELLED`, `UNKNOWN`, `INVALID_ARGUMENT`
352/// - `DEADLINE_EXCEEDED`, `NOT_FOUND`, `ALREADY_EXISTS`
353/// - `PERMISSION_DENIED`, `RESOURCE_EXHAUSTED`, `FAILED_PRECONDITION`
354/// - `ABORTED`, `OUT_OF_RANGE`, `UNIMPLEMENTED`, `INTERNAL`
355/// - `UNAVAILABLE`, `DATA_LOSS`, `UNAUTHENTICATED`
356#[proc_macro_derive(GrpcError, attributes(grpc))]
357pub fn grpc_error(input: TokenStream) -> TokenStream {
358 let input = proc_macro2::TokenStream::from(input);
359
360 error::grpc_error_impl(input)
361 .unwrap_or_else(|err| err.to_compile_error())
362 .into()
363}
364
365/// Derive macro for automatic HealthCheck implementation
366///
367/// Generates the `HealthCheck` trait implementation by collecting all fields
368/// that implement the `Dependency` trait.
369///
370/// # Example
371/// ```ignore
372/// use allframe_macros::HealthCheck;
373/// use allframe_core::health::{Dependency, DependencyStatus};
374///
375/// struct RedisDependency { /* ... */ }
376/// impl Dependency for RedisDependency { /* ... */ }
377///
378/// struct DatabaseDependency { /* ... */ }
379/// impl Dependency for DatabaseDependency { /* ... */ }
380///
381/// #[derive(HealthCheck)]
382/// struct AppHealth {
383/// #[health(timeout = "5s", critical = true)]
384/// redis: RedisDependency,
385///
386/// #[health(timeout = "10s", critical = false)]
387/// database: DatabaseDependency,
388///
389/// #[health(skip)]
390/// config: Config, // Not a dependency
391/// }
392///
393/// // Auto-generates:
394/// // impl HealthCheck for AppHealth { ... }
395/// ```
396///
397/// # Attributes
398/// - `#[health(skip)]` - Skip this field from health checks
399/// - `#[health(critical = true)]` - Mark dependency as critical (default: true)
400/// - `#[health(timeout = "5s")]` - Set check timeout (default: 5s)
401#[proc_macro_derive(HealthCheck, attributes(health))]
402pub fn health_check(input: TokenStream) -> TokenStream {
403 let input = proc_macro2::TokenStream::from(input);
404
405 health::health_check_impl(input)
406 .unwrap_or_else(|err| err.to_compile_error())
407 .into()
408}
409
410/// Derive macro for automatic Obfuscate implementation
411///
412/// Generates the `Obfuscate` trait implementation that safely logs struct
413/// fields, obfuscating those marked with `#[sensitive]`.
414///
415/// # Example
416/// ```ignore
417/// use allframe_macros::Obfuscate;
418///
419/// #[derive(Obfuscate)]
420/// struct DatabaseConfig {
421/// host: String,
422/// port: u16,
423/// #[sensitive]
424/// password: String,
425/// #[sensitive]
426/// api_key: String,
427/// }
428///
429/// let config = DatabaseConfig {
430/// host: "localhost".to_string(),
431/// port: 5432,
432/// password: "secret".to_string(),
433/// api_key: "sk_live_abc123".to_string(),
434/// };
435///
436/// // Output: "DatabaseConfig { host: "localhost", port: 5432, password: ***, api_key: *** }"
437/// println!("{}", config.obfuscate());
438/// ```
439///
440/// # Attributes
441/// - `#[sensitive]` - Mark field as sensitive, will be displayed as `***`
442/// - `#[obfuscate(with = "function_name")]` - Use custom function to obfuscate
443#[proc_macro_derive(Obfuscate, attributes(sensitive, obfuscate))]
444pub fn obfuscate(input: TokenStream) -> TokenStream {
445 let input = proc_macro2::TokenStream::from(input);
446
447 security::obfuscate_impl(input)
448 .unwrap_or_else(|err| err.to_compile_error())
449 .into()
450}
451
452/// Attribute macro for automatic retry with exponential backoff
453///
454/// Wraps an async function with retry logic using `RetryExecutor`.
455///
456/// # Example
457/// ```ignore
458/// use allframe_macros::retry;
459///
460/// #[retry(max_retries = 3, initial_interval_ms = 100)]
461/// async fn fetch_data() -> Result<String, std::io::Error> {
462/// // This will be retried up to 3 times on failure
463/// reqwest::get("https://api.example.com/data")
464/// .await?
465/// .text()
466/// .await
467/// }
468/// ```
469///
470/// # Parameters
471/// - `max_retries` - Maximum retry attempts (default: 3)
472/// - `initial_interval_ms` - Initial backoff in milliseconds (default: 500)
473/// - `max_interval_ms` - Maximum backoff in milliseconds (default: 30000)
474/// - `multiplier` - Backoff multiplier (default: 2.0)
475#[proc_macro_attribute]
476pub fn retry(attr: TokenStream, item: TokenStream) -> TokenStream {
477 let attr = proc_macro2::TokenStream::from(attr);
478 let item = proc_macro2::TokenStream::from(item);
479
480 resilience::retry_impl(attr, item)
481 .unwrap_or_else(|err| err.to_compile_error())
482 .into()
483}
484
485/// Attribute macro for circuit breaker pattern
486///
487/// Wraps a function with circuit breaker logic for fail-fast behavior.
488///
489/// # Example
490/// ```ignore
491/// use allframe_macros::circuit_breaker;
492///
493/// #[circuit_breaker(name = "external_api", failure_threshold = 5)]
494/// async fn call_external_api() -> Result<String, std::io::Error> {
495/// // After 5 failures, the circuit opens and calls fail fast
496/// external_service::call().await
497/// }
498/// ```
499///
500/// # Parameters
501/// - `name` - Circuit breaker name (default: function name)
502/// - `failure_threshold` - Failures before opening (default: 5)
503/// - `success_threshold` - Successes to close in half-open (default: 3)
504/// - `timeout_ms` - Time before half-open in milliseconds (default: 30000)
505#[proc_macro_attribute]
506pub fn circuit_breaker(attr: TokenStream, item: TokenStream) -> TokenStream {
507 let attr = proc_macro2::TokenStream::from(attr);
508 let item = proc_macro2::TokenStream::from(item);
509
510 resilience::circuit_breaker_impl(attr, item)
511 .unwrap_or_else(|err| err.to_compile_error())
512 .into()
513}
514
515/// Attribute macro for rate limiting
516///
517/// Wraps a function with rate limiting using token bucket algorithm.
518///
519/// # Example
520/// ```ignore
521/// use allframe_macros::rate_limited;
522///
523/// #[rate_limited(rps = 100, burst = 10)]
524/// fn handle_request() -> Result<Response, std::io::Error> {
525/// // Limited to 100 requests per second with burst of 10
526/// process_request()
527/// }
528/// ```
529///
530/// # Parameters
531/// - `rps` - Requests per second (default: 100)
532/// - `burst` - Burst capacity (default: 10)
533#[proc_macro_attribute]
534pub fn rate_limited(attr: TokenStream, item: TokenStream) -> TokenStream {
535 let attr = proc_macro2::TokenStream::from(attr);
536 let item = proc_macro2::TokenStream::from(item);
537
538 resilience::rate_limited_impl(attr, item)
539 .unwrap_or_else(|err| err.to_compile_error())
540 .into()
541}
542
543#[cfg(test)]
544mod tests {
545 #[test]
546 fn test_macros_crate_compiles() {
547 assert!(true);
548 }
549}