Skip to main content

reinhardt_macros/
lib.rs

1//! # Reinhardt Procedural Macros
2//!
3//! Provides Django-style decorators as Rust procedural macros.
4//!
5//! ## Macros
6//!
7//! - `#[routes]` - Register URL pattern function for automatic discovery
8//! - `#[api_view]` - Convert function to API view
9//! - `#[action]` - Define custom ViewSet action
10//! - `#[get]`, `#[post]`, etc. - HTTP method decorators
11//! - `#[permission_required]` - Permission decorator
12//!
13
14use proc_macro::TokenStream;
15use syn::{ItemFn, ItemStruct, parse_macro_input};
16
17mod action;
18mod admin;
19mod api_view;
20// client_routes module removed: superseded by #[url_patterns(InstalledApp::<variant>, mode = client)]
21mod app_config_attribute;
22mod app_config_derive;
23mod apply_update_attribute;
24mod apply_update_derive;
25mod collect_migrations;
26mod crate_paths;
27mod dto;
28mod flatten_imports;
29mod hook;
30mod injectable_common;
31mod injectable_fn;
32mod injectable_struct;
33mod installed_apps;
34mod macro_state;
35mod model_attribute;
36mod model_derive;
37mod orm_reflectable_derive;
38mod pascal_case;
39mod path_macro;
40mod permission_macro;
41mod permissions;
42mod pk_shape;
43mod query_fields;
44mod receiver;
45mod rel;
46mod routes;
47mod routes_registration;
48mod schema;
49mod settings_compose;
50mod settings_fragment;
51pub(crate) mod settings_parser;
52mod streaming;
53mod streaming_patterns;
54mod use_inject;
55mod user_attribute;
56mod user_field_mapping;
57mod validate_derive;
58
59use action::action_impl;
60use admin::admin_impl;
61use api_view::api_view_impl;
62use app_config_attribute::app_config_attribute_impl;
63use apply_update_attribute::apply_update_attribute_impl;
64use apply_update_derive::apply_update_derive_impl;
65use injectable_fn::injectable_fn_impl;
66use injectable_struct::injectable_struct_impl;
67use installed_apps::installed_apps_impl;
68use model_attribute::model_attribute_impl;
69use model_derive::model_derive_impl;
70use orm_reflectable_derive::orm_reflectable_derive_impl;
71use path_macro::path_impl;
72use permissions::permission_required_impl;
73use query_fields::derive_query_fields_impl;
74use receiver::receiver_impl;
75use routes::{delete_impl, get_impl, patch_impl, post_impl, put_impl};
76use routes_registration::routes_impl;
77mod url_patterns;
78mod viewset_macro;
79mod websocket;
80use schema::derive_schema_impl;
81use url_patterns::url_patterns_impl;
82use use_inject::use_inject_impl;
83use user_attribute::user_attribute_impl;
84
85/// Decorator for function-based API views
86#[proc_macro_attribute]
87pub fn api_view(args: TokenStream, input: TokenStream) -> TokenStream {
88	let input = parse_macro_input!(input as ItemFn);
89
90	api_view_impl(args.into(), input)
91		.unwrap_or_else(|e| e.to_compile_error())
92		.into()
93}
94
95/// Decorator for ViewSet custom actions
96#[proc_macro_attribute]
97pub fn action(args: TokenStream, input: TokenStream) -> TokenStream {
98	let input = parse_macro_input!(input as ItemFn);
99
100	action_impl(args.into(), input)
101		.unwrap_or_else(|e| e.to_compile_error())
102		.into()
103}
104
105/// GET method decorator
106#[proc_macro_attribute]
107pub fn get(args: TokenStream, input: TokenStream) -> TokenStream {
108	let input = parse_macro_input!(input as ItemFn);
109
110	get_impl(args.into(), input)
111		.unwrap_or_else(|e| e.to_compile_error())
112		.into()
113}
114
115/// POST method decorator
116#[proc_macro_attribute]
117pub fn post(args: TokenStream, input: TokenStream) -> TokenStream {
118	let input = parse_macro_input!(input as ItemFn);
119
120	post_impl(args.into(), input)
121		.unwrap_or_else(|e| e.to_compile_error())
122		.into()
123}
124
125/// PUT method decorator
126#[proc_macro_attribute]
127pub fn put(args: TokenStream, input: TokenStream) -> TokenStream {
128	let input = parse_macro_input!(input as ItemFn);
129
130	put_impl(args.into(), input)
131		.unwrap_or_else(|e| e.to_compile_error())
132		.into()
133}
134
135/// PATCH method decorator
136#[proc_macro_attribute]
137pub fn patch(args: TokenStream, input: TokenStream) -> TokenStream {
138	let input = parse_macro_input!(input as ItemFn);
139
140	patch_impl(args.into(), input)
141		.unwrap_or_else(|e| e.to_compile_error())
142		.into()
143}
144
145/// DELETE method decorator
146#[proc_macro_attribute]
147pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream {
148	let input = parse_macro_input!(input as ItemFn);
149
150	delete_impl(args.into(), input)
151		.unwrap_or_else(|e| e.to_compile_error())
152		.into()
153}
154
155/// Producer handler decorator — auto-publishes return value to a Kafka topic.
156///
157/// # Arguments
158///
159/// - `topic` — Kafka topic to publish to
160/// - `name` — identifier used in `ResolvedUrls::streaming().<app>().<name>()`
161///
162/// # Example
163///
164/// ```rust,ignore
165/// #[producer(topic = "orders", name = "create_order")]
166/// pub async fn create_order(cmd: CreateOrderCommand) -> Result<Order, StreamingError> {
167///     Ok(Order::from(cmd))
168/// }
169/// ```
170#[proc_macro_attribute]
171pub fn producer(args: TokenStream, input: TokenStream) -> TokenStream {
172	let input = parse_macro_input!(input as ItemFn);
173	streaming::producer_impl(args.into(), input)
174		.unwrap_or_else(|e| e.to_compile_error())
175		.into()
176}
177
178/// Consumer handler decorator — receives messages from a Kafka topic.
179///
180/// # Arguments
181///
182/// - `topic` — Kafka topic to consume from
183/// - `group` — Consumer group id
184/// - `name` — identifier used in `ResolvedUrls::streaming().<app>().<name>()`
185///
186/// # Example
187///
188/// ```rust,ignore
189/// #[consumer(topic = "orders", group = "order-processor", name = "handle_order")]
190/// pub async fn handle_order(msg: Message<Order>) -> Result<(), StreamingError> {
191///     Ok(())
192/// }
193/// ```
194#[proc_macro_attribute]
195pub fn consumer(args: TokenStream, input: TokenStream) -> TokenStream {
196	let input = parse_macro_input!(input as ItemFn);
197	streaming::consumer_impl(args.into(), input)
198		.unwrap_or_else(|e| e.to_compile_error())
199		.into()
200}
201
202/// Streaming patterns attribute — generates typed per-app streaming URL accessors.
203///
204/// Apply to the function that builds and returns the app's `StreamingRouter`.
205/// The function body must contain `streaming_routes![handler1, handler2, ...]`.
206///
207/// # Arguments
208///
209/// - First positional arg: `InstalledApp::<Variant>` — the app's label (or any path/ident)
210///
211/// # Example
212///
213/// ```rust,ignore
214/// #[streaming_patterns(InstalledApp::Orders)]
215/// pub fn streaming_routes() -> reinhardt_streaming::StreamingRouter {
216///     streaming_routes![create_order, handle_order]
217/// }
218/// ```
219///
220/// After this macro expands:
221/// - `OrdersStreamingUrls` struct is generated with `.create_order()` and `.handle_order()` methods
222/// - `urls.streaming().orders()` returns `OrdersStreamingUrls<'_>` (requires `#[routes]` in same crate)
223/// - Each method returns the Kafka topic name as `&'static str`
224#[proc_macro_attribute]
225pub fn streaming_patterns(args: TokenStream, input: TokenStream) -> TokenStream {
226	streaming_patterns::streaming_patterns_impl(args.into(), input.into())
227		.unwrap_or_else(|e| e.to_compile_error())
228		.into()
229}
230
231/// Permission required decorator
232#[proc_macro_attribute]
233pub fn permission_required(args: TokenStream, input: TokenStream) -> TokenStream {
234	let input = parse_macro_input!(input as ItemFn);
235
236	permission_required_impl(args.into(), input)
237		.unwrap_or_else(|e| e.to_compile_error())
238		.into()
239}
240
241/// Defines installed applications with compile-time validation.
242///
243/// Generates an `InstalledApp` enum with variants for each application,
244/// along with `Display`, `FromStr` traits and helper methods.
245///
246/// **Important**: This macro is for **user applications only**. Built-in framework features
247/// (auth, sessions, admin, etc.) are enabled via Cargo feature flags, not through `installed_apps!`.
248///
249/// # Generated Code
250///
251/// The macro generates:
252///
253/// - `enum InstalledApp { ... }` - Type-safe app references with variants for each app
254/// - `impl Display` - Convert enum variants to path strings
255/// - `impl FromStr` - Parse path strings to enum variants
256/// - `fn all_apps() -> Vec<String>` - List all app paths as strings
257/// - `fn path(&self) -> &'static str` - Get app path without allocation
258///
259/// # Example
260///
261/// ```rust,ignore
262/// use reinhardt::installed_apps;
263///
264/// installed_apps! {
265///     users: "users",
266///     posts: "posts",
267/// }
268///
269/// // Use generated enum
270/// let app = InstalledApp::users;
271/// println!("{}", app);  // Output: "users"
272///
273/// // Get all apps
274/// let all = InstalledApp::all_apps();
275/// assert_eq!(all, vec!["users".to_string(), "posts".to_string()]);
276///
277/// // Parse from string
278/// use std::str::FromStr;
279/// let app = InstalledApp::from_str("users")?;
280/// assert_eq!(app, InstalledApp::users);
281///
282/// // Get path without allocation
283/// assert_eq!(app.path(), "users");
284/// ```
285///
286/// # Compile-time Validation
287///
288/// Framework modules (starting with `reinhardt.`) are validated at compile time.
289/// Non-existent modules will cause compilation errors:
290///
291/// ```rust,ignore
292/// installed_apps! {
293///     nonexistent: "reinhardt.contrib.nonexistent",
294/// }
295/// // Compile error: cannot find module `nonexistent` in `contrib`
296/// ```
297///
298/// User apps (not starting with `reinhardt.`) skip compile-time validation,
299/// allowing flexible user-defined application names.
300///
301/// # Framework Features
302///
303/// **Do NOT use this macro for built-in framework features.** Instead, enable them
304/// via Cargo feature flags:
305///
306/// ```toml
307/// [dependencies]
308/// reinhardt = { version = "0.1.0-alpha.1", features = ["auth", "sessions", "admin"] }
309/// ```
310///
311/// Then import them directly:
312///
313/// ```rust,ignore
314/// use reinhardt::auth::*;
315/// use reinhardt::auth::sessions::*;
316/// use reinhardt::admin::*;
317/// ```
318///
319/// # See Also
320///
321/// - Module documentation in `installed_apps.rs` for detailed information about
322///   generated code structure, trait implementations, and advanced usage
323/// - `crates/reinhardt-apps/README.md` for comprehensive usage guide
324/// - Tutorial: `docs/tutorials/en/basis/1-project-setup.md`
325///
326#[proc_macro]
327pub fn installed_apps(input: TokenStream) -> TokenStream {
328	installed_apps_impl(input.into())
329		.unwrap_or_else(|e| e.to_compile_error())
330		.into()
331}
332
333/// Register URL patterns for automatic discovery by the framework
334///
335/// This attribute macro automatically registers a function as the URL pattern
336/// provider for the framework. The function will be discovered and used when
337/// running management commands like `runserver`.
338///
339/// # Important: Single Usage Only
340///
341/// **Only one function per project can be annotated with `#[routes]`.**
342/// If multiple `#[routes]` attributes are used, the linker will fail with a
343/// "duplicate symbol" error for `__reinhardt_routes_registration_marker`.
344///
345/// To organize routes across multiple files, use the `.mount()` method:
346///
347/// ```rust,ignore
348/// // In src/config/urls.rs - Only ONE #[routes] in the entire project
349/// #[routes]
350/// pub fn routes() -> UnifiedRouter {
351///     UnifiedRouter::new()
352///         .mount("/api/", api::routes())   // api::routes() returns UnifiedRouter
353///         .mount("/admin/", admin::routes())  // WITHOUT #[routes] attribute
354/// }
355///
356/// // In src/apps/api/urls.rs - NO #[routes] attribute
357/// pub fn routes() -> UnifiedRouter {
358///     UnifiedRouter::new()
359///         .endpoint(views::list)
360///         .endpoint(views::create)
361/// }
362/// ```
363///
364/// # Supported Function Signatures
365///
366/// ## 1. Sync function (standard)
367///
368/// ```rust,ignore
369/// #[routes]
370/// pub fn routes() -> UnifiedRouter {
371///     UnifiedRouter::new()
372///         .endpoint(views::index)
373///         .mount("/api/", api::routes())
374/// }
375/// ```
376///
377/// ## 2. Async function (no DI)
378///
379/// ```rust,ignore
380/// #[routes]
381/// pub async fn routes() -> UnifiedRouter {
382///     UnifiedRouter::new()
383///         .mount("/api/", api::routes())
384/// }
385/// ```
386///
387/// ## 3. Async function with `#[inject]` (DI-aware)
388///
389/// ```rust,ignore
390/// #[routes]
391/// pub async fn routes(#[inject] router: UnifiedRouter) -> UnifiedRouter {
392///     router
393/// }
394/// ```
395///
396/// When `#[inject]` parameters are present, the macro automatically creates
397/// a DI context (`SingletonScope` + `InjectionContext`) and resolves each
398/// injected dependency before calling the function.
399///
400/// # Arguments
401///
402/// - `#[routes]` — Default mode. Generates `ResolvedUrls` and `url_prelude`
403///   module with URL resolver traits from all installed apps. Requires
404///   `installed_apps!` to be present in the crate.
405/// - `#[routes(standalone)]` — Standalone mode. Generates `ResolvedUrls` only,
406///   without `url_prelude` module. Use this for projects that don't use
407///   `installed_apps!`.
408/// - `#[routes(client_inventory)]` — Opt into the WASM cross-target
409///   `ClientRouter` `inventory::submit!` registration (see Issue #4453).
410///
411/// ## Per-mode resolver suppression (Issue #4509)
412///
413/// Plain `#[routes]` unconditionally emits per-app resolver lookups for
414/// three modes (server, client, ws). REST-only apps that do not use
415/// `#[url_patterns(InstalledApp::<app>, mode = client)]` or
416/// `#[url_patterns(InstalledApp::<app>, mode = ws)]` would need to stub
417/// the missing modules. These flags let the macro skip the unused lookups:
418///
419/// - `#[routes(server_only)]` — Shorthand for
420///   `no_client_resolvers, no_ws_resolvers`. The `urls.client()` /
421///   `urls.ws()` gateways are not emitted; only `urls.server().<app>()`
422///   remains. The `ResolvedUrls::<app>()` server-side accessor is
423///   preserved (unlike `standalone`, which suppresses it).
424/// - `#[routes(no_client_resolvers)]` — Skip per-app client resolver
425///   lookups and the `ClientUrls` / `<app>ClientUrls` types.
426/// - `#[routes(no_ws_resolvers)]` — Skip per-app WebSocket resolver
427///   lookups, the `WsUrls` / `<app>WsUrls` types, and the stub
428///   `WebSocketUrlResolver` impl on `ResolvedUrls`.
429///
430/// `client_inventory` is mutually exclusive with the `no_*` flags
431/// (and therefore with `server_only`) — `client_inventory` registers
432/// the very WASM client surface those flags suppress.
433///
434/// ```rust,ignore
435/// // REST-only project with #[url_patterns(InstalledApp::api, mode = server)]
436/// // — no stub client_router.rs / ws_urls.rs needed. `server_only` only
437/// // gates per-app client/ws emission, so the return type stays
438/// // `UnifiedRouter` for consistency with every other `#[routes]` example.
439/// #[routes(server_only)]
440/// pub fn routes() -> UnifiedRouter {
441///     UnifiedRouter::new().mount("/api/", api::urls::url_patterns())
442/// }
443///
444/// // HTTP + WebSocket but no WASM admin: skip client resolvers only.
445/// #[routes(no_client_resolvers)]
446/// pub fn routes() -> UnifiedRouter { ... }
447/// ```
448///
449/// # Notes
450///
451/// - The function can have any name (e.g., `routes`, `app_routes`, `url_patterns`)
452/// - The return type must be `UnifiedRouter` (not `Arc<UnifiedRouter>`)
453/// - The framework automatically wraps the router in `Arc`
454/// - Sync functions cannot use `#[inject]` (DI resolution is inherently async)
455#[proc_macro_attribute]
456pub fn routes(args: TokenStream, input: TokenStream) -> TokenStream {
457	let input = parse_macro_input!(input as ItemFn);
458
459	routes_impl(args.into(), input)
460		.unwrap_or_else(|e| e.to_compile_error())
461		.into()
462}
463
464/// Register an app-scoped URL pattern function with the framework and
465/// generate per-mode helper modules from its body.
466///
467/// # Syntax
468///
469/// ```text
470/// #[url_patterns(<InstalledApp::variant>, mode = server|client|unified|ws)]
471/// pub fn url_patterns() -> Router { ... }
472/// ```
473///
474/// The first argument is a path to a variant of an enum implementing
475/// `reinhardt_apps::apps::AppLabel` — normally the `InstalledApp`
476/// enum generated by `installed_apps!`. The macro wraps the original
477/// function so the returned router carries the variant's
478/// `AppLabel::path(&variant)` value as its runtime namespace.
479///
480/// # Modes
481///
482/// | mode      | Router          | Scanned calls                                                            | Modules emitted                                    |
483/// |-----------|-----------------|--------------------------------------------------------------------------|----------------------------------------------------|
484/// | `server`  | `ServerRouter`  | `.endpoint()`, `.viewset()`, `.mount()`                                  | `url_resolvers`                                    |
485/// | `client`  | `ClientRouter`  | `.named_route()` / `.named_route_path*()` family                         | `client_url_resolvers` + `urls` (typed helpers)    |
486/// | `unified` | `UnifiedRouter` | both sides (inside `.server(\|s\| ...)` / `.client(\|c\| ...)` closures) | `url_resolvers` + `client_url_resolvers` + `urls`  |
487/// | `ws`      | `WsRouter`      | `.consumer(...)`                                                         | `ws_url_resolvers`                                 |
488///
489/// # The `urls` module (Issue #4644)
490///
491/// For `mode = client` and the client side of `mode = unified`, the macro
492/// emits a sibling `pub mod urls` whose contents mirror the named routes
493/// declared in the function body. Each named route appears as a free
494/// function whose signature carries the path parameters lifted from the
495/// closure binding:
496///
497/// ```text
498/// // Input
499/// #[url_patterns(InstalledApp::polls, mode = client)]
500/// pub fn client_url_patterns() -> ClientRouter {
501///     ClientRouter::new()
502///         .named_route("index", "/", index_page)
503///         .named_route_path(
504///             "detail",
505///             "/polls/{question_id}/",
506///             |ClientPath(question_id): ClientPath<i64>| polls_detail_page(question_id),
507///         )
508/// }
509///
510/// // Generated (sketch)
511/// pub mod urls {
512///     pub fn index() -> String { /* "polls:index" via global reverser */ }
513///     pub fn detail(question_id: i64) -> String { /* "polls:detail" */ }
514/// }
515/// ```
516///
517/// Each helper resolves through the global `ClientUrlReverser`
518/// (`reinhardt::get_client_reverser()`), so a route-pattern change in the
519/// `#[url_patterns]` body propagates without any call-site edits.
520///
521/// **Extraction rules.** A typed helper is generated when the path
522/// parameter count in the pattern matches the binding count in the
523/// closure, and every binding is shaped `ClientPath(name): ClientPath<T>`:
524///
525/// - `.named_route(name, pattern, component)` — always emits a zero-arg
526///   helper.
527/// - `.named_route_path(name, pattern, |ClientPath(id): ClientPath<T>| ...)`
528///   — emits `fn name(id: T) -> String`.
529/// - `.named_route_path2(...)` / `.named_route_path3(...)` — emit helpers
530///   with two / three positional parameters, in binding order.
531/// - `.named_route_params(...)` / `.named_route_result(...)` — the macro
532///   does not project these into typed helpers today; the route is still
533///   reachable through the stringly-typed
534///   `ResolvedUrls::resolve_client_url(...)` API.
535/// - Routes whose handler is a function reference (not an inline closure)
536///   are silently skipped in the `urls` module for the same reason.
537///
538/// The `urls` module is emitted unconditionally so call sites can write
539/// `use ...::urls;` regardless of which routes happen to be typed; an
540/// empty module is harmless.
541#[doc = include_str!("upstream_workaround_note.md")]
542#[proc_macro_attribute]
543pub fn url_patterns(args: TokenStream, input: TokenStream) -> TokenStream {
544	url_patterns_impl(args.into(), input.into())
545		.unwrap_or_else(|e| e.to_compile_error())
546		.into()
547}
548
549/// Generate URL resolver traits for a ViewSet function.
550///
551/// When applied to a function returning a ViewSet (e.g., `ModelViewSet`), extracts
552/// the basename from the function body and generates `__url_resolver_{basename}_list`
553/// and `__url_resolver_{basename}_detail` modules.
554///
555/// # Example
556///
557/// ```rust,ignore
558/// #[viewset]
559/// pub fn viewset() -> ModelViewSet<Snippet, SnippetSerializer> {
560///     ModelViewSet::new("snippet")
561/// }
562/// // Generates: __url_resolver_snippet_list, __url_resolver_snippet_detail
563/// ```
564///
565#[doc = include_str!("upstream_workaround_note.md")]
566#[proc_macro_attribute]
567pub fn viewset(args: TokenStream, input: TokenStream) -> TokenStream {
568	viewset_macro::viewset_macro_impl(args.into(), input.into())
569		.unwrap_or_else(|e| e.to_compile_error())
570		.into()
571}
572
573/// Validate URL patterns at compile time
574///
575/// This macro validates URL pattern syntax at compile time, catching common errors
576/// before they reach runtime. It supports both simple parameters and Django-style
577/// typed parameters.
578///
579/// # Compile-time Validation
580///
581/// The macro will fail to compile if:
582/// - Braces are not properly matched (e.g., `{id` or `id}`)
583/// - Parameter names are empty (e.g., `{}`)
584/// - Parameter names contain invalid characters
585/// - Type specifiers are invalid (valid: `int`, `str`, `uuid`, `slug`, `path`)
586/// - Django-style parameters are used outside braces (e.g., `<int:id>` instead of `{<int:id>}`)
587///
588/// # Supported Type Specifiers
589///
590/// - `int` - Integer values
591/// - `str` - String values
592/// - `uuid` - UUID values
593/// - `slug` - Slug strings (alphanumeric, hyphens, underscores)
594/// - `path` - Path segments (can include slashes)
595///
596#[proc_macro]
597pub fn path(input: TokenStream) -> TokenStream {
598	path_impl(input.into())
599		.unwrap_or_else(|e| e.to_compile_error())
600		.into()
601}
602
603/// Connect a receiver function to a signal automatically
604///
605/// This macro provides Django-style `@receiver` decorator functionality for Rust.
606/// It automatically registers the function as a signal receiver at startup.
607///
608#[proc_macro_attribute]
609pub fn receiver(args: TokenStream, input: TokenStream) -> TokenStream {
610	let input = parse_macro_input!(input as ItemFn);
611
612	receiver_impl(args.into(), input)
613		.unwrap_or_else(|e| e.to_compile_error())
614		.into()
615}
616
617/// Attribute macro for registering lifecycle hooks.
618///
619/// Currently supports `runserver` hooks for extending server startup behavior.
620///
621/// # Usage
622///
623/// ```rust,ignore
624/// use reinhardt::commands::{RunserverHook, RunserverContext};
625///
626/// #[reinhardt::hook(on = runserver)]
627/// struct MyValidationHook;
628///
629/// #[async_trait]
630/// impl RunserverHook for MyValidationHook {
631///     async fn validate(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
632///         // Fail-fast validation before server starts
633///         Ok(())
634///     }
635/// }
636/// ```
637#[proc_macro_attribute]
638pub fn hook(args: TokenStream, input: TokenStream) -> TokenStream {
639	let input = parse_macro_input!(input as ItemStruct);
640	hook::hook_impl(args.into(), input)
641		.unwrap_or_else(|e| e.to_compile_error())
642		.into()
643}
644
645/// Automatic dependency injection macro
646///
647/// This macro enables FastAPI-style dependency injection using parameter attributes.
648/// Parameters marked with `#[inject]` will be automatically resolved from the
649/// `InjectionContext`. Can be used with any function, not just endpoints.
650///
651/// # Generated Code
652///
653/// The macro transforms the function by:
654/// 1. Removing `#[inject]` parameters from the signature
655/// 2. Adding an `InjectionContext` parameter
656/// 3. Injecting dependencies at the start of the function
657///
658#[proc_macro_attribute]
659pub fn use_inject(args: TokenStream, input: TokenStream) -> TokenStream {
660	let input = parse_macro_input!(input as ItemFn);
661
662	use_inject_impl(args.into(), input)
663		.unwrap_or_else(|e| e.to_compile_error())
664		.into()
665}
666
667/// Derive macro for type-safe field lookups
668///
669/// Automatically generates field accessor methods for models, enabling
670/// compile-time validated field lookups.
671///
672/// # Generated Methods
673///
674/// For each field in the struct, the macro generates a static method that
675/// returns a `Field<Model, FieldType>`. The field type determines which
676/// lookup methods are available:
677///
678/// - String fields: `lower()`, `upper()`, `trim()`, `contains()`, etc.
679/// - Numeric fields: `abs()`, `ceil()`, `floor()`, `round()`
680/// - DateTime fields: `year()`, `month()`, `day()`, `hour()`, etc.
681/// - All fields: `eq()`, `ne()`, `gt()`, `gte()`, `lt()`, `lte()`
682///
683#[proc_macro_derive(QueryFields)]
684pub fn derive_query_fields(input: TokenStream) -> TokenStream {
685	let input = parse_macro_input!(input as syn::DeriveInput);
686
687	derive_query_fields_impl(input)
688		.unwrap_or_else(|e| e.to_compile_error())
689		.into()
690}
691
692/// Derive macro for automatic OpenAPI schema generation
693///
694/// Automatically implements the `ToSchema` trait for structs and enums,
695/// generating OpenAPI 3.0 schemas from Rust type definitions.
696///
697/// # Supported Types
698///
699/// - Primitives: `String`, `i32`, `i64`, `f32`, `f64`, `bool`
700/// - `Option<T>`: Makes fields optional in the schema
701/// - `Vec<T>`: Generates array schemas
702/// - Custom types implementing `ToSchema`
703///
704/// # Features
705///
706/// - Automatic field metadata extraction
707/// - Documentation comments become field descriptions
708/// - Required/optional field detection
709/// - Nested schema support
710/// - Enum variant handling
711///
712#[proc_macro_derive(Schema)]
713pub fn derive_schema(input: TokenStream) -> TokenStream {
714	let input = parse_macro_input!(input as syn::DeriveInput);
715
716	derive_schema_impl(input)
717		.unwrap_or_else(|e| e.to_compile_error())
718		.into()
719}
720
721/// Attribute macro for injectable factory/provider functions and structs
722///
723/// This macro can be applied to both functions and structs to enable dependency injection.
724///
725/// # Field Attributes (Struct Only)
726///
727/// All struct fields must have either `#[inject]` or `#[no_inject]` attribute:
728///
729/// - **`#[inject]`**: Inject this field from the DI container
730/// - **`#[inject(cache = false)]`**: Inject without caching
731/// - **`#[inject(scope = Singleton)]`**: Use singleton scope
732/// - **`#[no_inject(default = Default)]`**: Initialize with `Default::default()`
733/// - **`#[no_inject(default = value)]`**: Initialize with specific value
734/// - **`#[no_inject]`**: Initialize with `None` (field must be `Option<T>`)
735///
736/// # Restrictions
737///
738/// **For functions:**
739/// - Function must have an explicit return type
740/// - All parameters must be marked with `#[inject]`
741///
742/// **For structs:**
743/// - Struct must have named fields
744/// - All fields must have either `#[inject]` or `#[no_inject]` attribute
745/// - `#[no_inject]` without default value requires field type to be `Option<T>`
746/// - `Clone` is auto-derived if not already present (used by `into_inner()` and `injectable_factory` patterns)
747/// - All `#[inject]` field types must implement `Injectable`
748///
749/// # Attribute Ordering
750///
751/// **`#[injectable]` must be placed above `#[derive(...)]` attributes.**
752///
753/// In Rust 2024 edition, attribute macros can only see attributes listed
754/// below them. If `#[derive(Clone)]` appears above `#[injectable]`, the
755/// macro cannot detect it and will add a duplicate `#[derive(Clone)]`,
756/// causing a compilation error.
757///
758/// ```ignore
759/// // Correct
760/// #[injectable]
761/// #[derive(Default, Debug)]
762/// struct MyService { /* ... */ }
763///
764/// // Incorrect — may cause duplicate Clone derive
765/// #[derive(Default, Debug)]
766/// #[injectable]
767/// struct MyService { /* ... */ }
768/// ```
769///
770#[proc_macro_attribute]
771pub fn injectable(args: TokenStream, input: TokenStream) -> TokenStream {
772	// Try to parse as ItemFn first
773	if let Ok(item_fn) = syn::parse::<ItemFn>(input.clone()) {
774		return injectable_fn_impl(proc_macro2::TokenStream::new(), item_fn)
775			.unwrap_or_else(|e| e.to_compile_error())
776			.into();
777	}
778
779	// Try to parse as ItemStruct
780	if let Ok(item_struct) = syn::parse::<ItemStruct>(input.clone()) {
781		// Convert ItemStruct to DeriveInput for compatibility
782		let derive_input = syn::DeriveInput {
783			attrs: item_struct.attrs,
784			vis: item_struct.vis,
785			ident: item_struct.ident,
786			generics: item_struct.generics,
787			data: syn::Data::Struct(syn::DataStruct {
788				struct_token: item_struct.struct_token,
789				fields: item_struct.fields,
790				semi_token: item_struct.semi_token,
791			}),
792		};
793
794		return injectable_struct_impl(args.into(), derive_input)
795			.unwrap_or_else(|e| e.to_compile_error())
796			.into();
797	}
798
799	// Neither ItemFn nor ItemStruct
800	syn::Error::new(
801		proc_macro2::Span::call_site(),
802		"#[injectable] can only be applied to functions or structs",
803	)
804	.to_compile_error()
805	.into()
806}
807
808/// Attribute macro for Django-style model definition with automatic derive
809///
810/// Automatically adds `#[derive(Model)]` and keeps the `#[model(...)]` attribute.
811/// This provides a cleaner syntax by eliminating the need to explicitly write
812/// `#[derive(Model)]` on every model struct.
813///
814/// # Model Attributes
815///
816/// Same as `#[derive(Model)]`. See [`derive_model`] for details.
817///
818#[proc_macro_attribute]
819pub fn model(args: TokenStream, input: TokenStream) -> TokenStream {
820	let input = parse_macro_input!(input as ItemStruct);
821
822	model_attribute_impl(args.into(), input)
823		.unwrap_or_else(|e| e.to_compile_error())
824		.into()
825}
826
827/// Attribute macro for generating auth trait implementations.
828///
829/// Generates `BaseUser`, `FullUser` (when `full = true`), `PermissionsMixin`
830/// (when `user_permissions` and `groups` fields exist), and `AuthIdentity`
831/// trait implementations based on struct fields.
832///
833/// # Arguments
834///
835/// - `hasher`: Type implementing `PasswordHasher + Default` (required)
836/// - `username_field`: Name of the field used as username (required)
837/// - `full`: Generate `FullUser` impl (default: `false`)
838///
839/// # Examples
840///
841/// ```rust,ignore
842/// #[user(hasher = Argon2Hasher, username_field = "email", full = true)]
843/// #[derive(Serialize, Deserialize)]
844/// pub struct MyUser {
845///     pub id: Uuid,
846///     pub email: String,
847///     pub password_hash: Option<String>,
848///     pub last_login: Option<DateTime<Utc>>,
849///     pub is_active: bool,
850///     pub is_superuser: bool,
851/// }
852/// ```
853#[proc_macro_attribute]
854pub fn user(args: TokenStream, input: TokenStream) -> TokenStream {
855	let input = parse_macro_input!(input as ItemStruct);
856
857	user_attribute_impl(args.into(), input)
858		.unwrap_or_else(|e| e.to_compile_error())
859		.into()
860}
861
862/// Derive macro for automatic Model implementation and migration registration
863///
864/// Automatically implements the `Model` trait and registers the model with the global
865/// ModelRegistry for automatic migration generation.
866///
867/// # Model Attributes
868///
869/// - `app_label`: Application label (default: "default")
870/// - `table_name`: Database table name (default: struct name in snake_case)
871/// - `constraints`: List of unique constraints (e.g., `unique(fields = ["field1", "field2"], name = "name")`)
872///
873/// # Field Attributes
874///
875/// - `primary_key`: Mark field as primary key (required for exactly one field)
876/// - `max_length`: Maximum length for String fields (required for String)
877/// - `null`: Allow NULL values (default: inferred from `Option<T>`)
878/// - `blank`: Allow blank values in forms
879/// - `unique`: Enforce uniqueness constraint
880/// - `default`: Default value
881/// - `db_column`: Custom database column name
882/// - `editable`: Whether field is editable (default: true)
883///
884/// # Supported Types
885///
886/// - `i32` → IntegerField
887/// - `i64` → BigIntegerField
888/// - `String` → CharField (requires max_length)
889/// - `bool` → BooleanField
890/// - `DateTime<Utc>` → DateTimeField
891/// - `Date` → DateField
892/// - `Time` → TimeField
893/// - `f32`, `f64` → FloatField
894/// - `Option<T>` → Sets null=true automatically
895///
896/// # Requirements
897///
898/// - Struct must have named fields
899/// - Struct must implement `Serialize` and `Deserialize`
900/// - Exactly one field must be marked with `primary_key = true`
901/// - String fields must specify `max_length`
902///
903#[proc_macro_derive(Model, attributes(model, model_config, field, rel, fk_id_field))]
904pub fn derive_model(input: TokenStream) -> TokenStream {
905	let input = parse_macro_input!(input as syn::DeriveInput);
906
907	model_derive_impl(input)
908		.unwrap_or_else(|e| e.to_compile_error())
909		.into()
910}
911
912/// Derive macro for automatic OrmReflectable implementation
913///
914/// Automatically implements the `OrmReflectable` trait for structs,
915/// enabling reflection-based field and relationship access for association proxies.
916///
917/// ## Type Inference
918///
919/// Fields are automatically classified based on their types:
920/// - `Vec<T>` → Collection relationship
921/// - `Option<T>` (where T is non-primitive) → Scalar relationship
922/// - Primitive types (i32, String, etc.) → Regular fields
923///
924/// ## Attributes
925///
926/// Override automatic inference with explicit attributes:
927///
928/// - `#[orm_field(type = "Integer")]` - Mark as regular field with specific type
929/// - `#[orm_relationship(type = "collection")]` - Mark as collection relationship
930/// - `#[orm_relationship(type = "scalar")]` - Mark as scalar relationship
931/// - `#[orm_ignore]` - Exclude field from reflection
932///
933/// ## Supported Field Types
934///
935/// - **Integer**: i8, i16, i32, i64, i128, u8, u16, u32, u64, u128
936/// - **Float**: f32, f64
937/// - **Boolean**: bool
938/// - **String**: String, str
939///
940#[proc_macro_derive(OrmReflectable, attributes(orm_field, orm_relationship, orm_ignore))]
941pub fn derive_orm_reflectable(input: TokenStream) -> TokenStream {
942	orm_reflectable_derive_impl(input)
943}
944
945/// Attribute macro for Django-style AppConfig definition with automatic derive
946///
947/// Automatically adds `#[derive(AppConfig)]` and keeps the `#[app_config(...)]` attribute.
948/// This provides a cleaner syntax by eliminating the need to explicitly write
949/// `#[derive(AppConfig)]` on every app config struct.
950///
951/// # Example
952///
953/// ```rust,ignore
954/// #[app_config(name = "hello", label = "hello")]
955/// pub struct HelloConfig;
956///
957/// // Generates a config() method:
958/// let config = HelloConfig::config();
959/// assert_eq!(config.name, "hello");
960/// assert_eq!(config.label, "hello");
961/// ```
962///
963/// # Attributes
964///
965/// - `name`: Application name (required, string literal)
966/// - `label`: Application label (required, string literal)
967/// - `verbose_name`: Verbose name (optional, string literal)
968///
969/// # Note
970///
971/// Direct use of `#[derive(AppConfig)]` is not allowed. Always use
972/// `#[app_config(...)]` attribute macro instead.
973///
974#[proc_macro_attribute]
975pub fn app_config(args: TokenStream, input: TokenStream) -> TokenStream {
976	let input = parse_macro_input!(input as ItemStruct);
977
978	app_config_attribute_impl(args.into(), input)
979		.unwrap_or_else(|e| e.to_compile_error())
980		.into()
981}
982
983/// Derive macro for automatic AppConfig factory method generation
984///
985/// **Note**: Do not use this derive macro directly. Use `#[app_config(...)]`
986/// attribute macro instead.
987///
988/// This derive macro is invoked automatically by the `#[app_config(...)]` attribute.
989/// Direct use will result in a compile error.
990///
991#[proc_macro_derive(AppConfig, attributes(app_config, app_config_internal))]
992pub fn derive_app_config(input: TokenStream) -> TokenStream {
993	app_config_derive::derive(input)
994}
995
996/// Collect migrations and register them with the global registry
997///
998/// # Deprecated since 0.2.0
999///
1000/// **This macro is deprecated.** Use `FilesystemSource` instead for loading migrations.
1001/// `FilesystemSource` scans directories for `.rs` migration files and does not require
1002/// compile-time registration. It is consistent with `manage migrate` behavior and
1003/// works reliably in Cargo workspaces when using `env!("CARGO_MANIFEST_DIR")`.
1004///
1005/// This macro generates a `MigrationProvider` implementation and automatically
1006/// registers it with the global migration registry using `linkme::distributed_slice`.
1007///
1008/// # Requirements
1009///
1010/// - Each migration module must export a `migration()` function returning `Migration`
1011/// - The crate must have `reinhardt-migrations` and `linkme` as dependencies
1012///
1013#[proc_macro]
1014pub fn collect_migrations(input: TokenStream) -> TokenStream {
1015	collect_migrations::collect_migrations_impl(input.into())
1016		.unwrap_or_else(|e| e.to_compile_error())
1017		.into()
1018}
1019
1020/// Attribute macro for ModelAdmin configuration
1021///
1022/// Automatically implements the `ModelAdmin` trait for a struct with compile-time
1023/// field validation against the specified model type.
1024///
1025/// # Attributes
1026///
1027/// ## Required
1028///
1029/// - `for = ModelType` - The model type to validate fields against
1030/// - `name = "ModelName"` - The display name for the model
1031///
1032/// ## Optional
1033///
1034/// - `list_display = [field1, field2, ...]` - Fields to display in list view (default: `[id]`)
1035/// - `list_filter = [field1, field2, ...]` - Fields for filtering (default: `[]`)
1036/// - `search_fields = [field1, field2, ...]` - Fields for search (default: `[]`)
1037/// - `fields = [field1, field2, ...]` - Fields to display in forms (default: all)
1038/// - `readonly_fields = [field1, field2, ...]` - Read-only fields (default: `[]`)
1039/// - `ordering = [(field1, asc/desc), ...]` - Default ordering (default: `[(id, desc)]`)
1040/// - `list_per_page = N` - Items per page (default: site default)
1041///
1042/// # Compile-time Field Validation
1043///
1044/// All field names are validated at compile time against the model's `field_xxx()` methods.
1045/// If a field doesn't exist, compilation will fail with an error.
1046///
1047/// # Generated Code
1048///
1049/// The macro generates:
1050/// 1. The struct definition
1051/// 2. Compile-time field validation code
1052/// 3. `ModelAdmin` trait implementation with `#[async_trait]`
1053///
1054#[proc_macro_attribute]
1055pub fn admin(args: TokenStream, input: TokenStream) -> TokenStream {
1056	let input = parse_macro_input!(input as ItemStruct);
1057
1058	admin_impl(args.into(), input)
1059		.unwrap_or_else(|e| e.to_compile_error())
1060		.into()
1061}
1062
1063/// Attribute macro for applying partial updates to target structs
1064///
1065/// Automatically adds `#[derive(ApplyUpdate)]` and creates a helper config attribute.
1066/// This provides a cleaner syntax for defining update request structs.
1067///
1068/// # Attributes
1069///
1070/// - `target(Type1, Type2, ...)`: Target types to generate `ApplyUpdate` implementations for
1071///
1072/// # Field Attributes
1073///
1074/// - `#[apply_update(skip)]`: Skip this field during update application
1075/// - `#[apply_update(rename = "field_name")]`: Use a different field name on the target
1076///
1077#[proc_macro_attribute]
1078pub fn apply_update(args: TokenStream, input: TokenStream) -> TokenStream {
1079	let input = parse_macro_input!(input as ItemStruct);
1080
1081	apply_update_attribute_impl(args.into(), input)
1082		.unwrap_or_else(|e| e.to_compile_error())
1083		.into()
1084}
1085
1086/// Derive macro for automatic `ApplyUpdate` trait implementation
1087///
1088/// **Note**: Do not use this derive macro directly. Use `#[apply_update(...)]`
1089/// attribute macro instead.
1090///
1091#[proc_macro_derive(ApplyUpdate, attributes(apply_update, apply_update_config))]
1092pub fn derive_apply_update(input: TokenStream) -> TokenStream {
1093	let input = parse_macro_input!(input as syn::DeriveInput);
1094
1095	apply_update_derive_impl(input)
1096		.unwrap_or_else(|e| e.to_compile_error())
1097		.into()
1098}
1099
1100/// Derive macro for struct-level validation
1101///
1102/// Implements the `Validate` trait using `#[validate(...)]` field attributes
1103/// to call Reinhardt's built-in validators.
1104///
1105/// # Supported Attributes
1106///
1107/// - `#[validate(email)]` - Validate email format
1108/// - `#[validate(url)]` - Validate URL format
1109/// - `#[validate(length(min = N, max = M))]` - Validate string length
1110/// - `#[validate(range(min = N, max = M))]` - Validate numeric range
1111/// - `message = "..."` - Custom error message (inside rule parentheses)
1112///
1113/// `Option<T>` fields are skipped when `None`.
1114///
1115#[proc_macro_derive(Validate, attributes(validate))]
1116pub fn derive_validate(input: TokenStream) -> TokenStream {
1117	let input = parse_macro_input!(input as syn::DeriveInput);
1118
1119	validate_derive::validate_derive_impl(input)
1120		.unwrap_or_else(|e| e.to_compile_error())
1121		.into()
1122}
1123
1124/// Attribute macro that absorbs the `cfg_attr(native, ...)` boilerplate for
1125/// DTOs shared between the server (`native` cfg) and client (`wasm`) builds.
1126///
1127/// The macro:
1128///
1129/// 1. Emits `#[cfg_attr(native, derive(::reinhardt::Validate, ::reinhardt::rest::openapi::Schema))]`
1130///    on the struct so the server build gets validation and OpenAPI schema
1131///    generation, while the wasm build sees a plain serializable type.
1132/// 2. Wraps every `#[validate(...)]` field attribute in `#[cfg_attr(native, ...)]`
1133///    so the same source compiles unchanged for `wasm32-unknown-unknown`.
1134/// 3. Is idempotent: if the user already wrote
1135///    `#[cfg_attr(native, derive(Validate))]` or
1136///    `#[cfg_attr(native, derive(Schema))]` on the struct, that derive is not
1137///    duplicated.
1138///
1139/// # `#[dto]` vs [`macro@model`]
1140///
1141/// Both are struct attributes, but they describe different things and live in
1142/// different files:
1143///
1144/// | | `#[model]` (ORM) | `#[dto]` (this macro) |
1145/// |---|---|---|
1146/// | What | A persistent record | A wire-level data shape |
1147/// | Where it lives | `apps/<app>/models/*.rs` | `apps/<app>/shared/types.rs` |
1148/// | Where it runs | Server only (`native`) | Both server (`native`) and client (`wasm`) |
1149/// | What it adds | Table mapping, primary key, FK fields, migrations | Validate + OpenAPI `Schema` derives (native-only), wraps `#[validate(...)]` |
1150/// | Boundary it crosses | Rust ↔ database | Server ↔ client (via `#[server_fn]`, REST handlers, WebSocket payloads) |
1151///
1152/// "DTO" is the industry-standard term for the second row — a data-transfer
1153/// object that is serialized on one side, sent over the wire, and
1154/// deserialized on the other side.
1155///
1156/// # Example
1157///
1158/// ```rust,ignore
1159/// use reinhardt::dto;
1160/// use serde::{Deserialize, Serialize};
1161///
1162/// #[dto]
1163/// #[derive(Debug, Clone, Serialize, Deserialize)]
1164/// pub struct LoginRequest {
1165///     #[validate(email(message = "Invalid email address"))]
1166///     pub email: String,
1167///
1168///     #[validate(length(min = 1, message = "Password is required"))]
1169///     pub password: String,
1170/// }
1171/// ```
1172///
1173/// Expands (conceptually) to:
1174///
1175/// ```rust,ignore
1176/// #[cfg_attr(native, derive(::reinhardt::Validate, ::reinhardt::rest::openapi::Schema))]
1177/// #[derive(Debug, Clone, Serialize, Deserialize)]
1178/// pub struct LoginRequest {
1179///     #[cfg_attr(native, validate(email(message = "Invalid email address")))]
1180///     pub email: String,
1181///
1182///     #[cfg_attr(native, validate(length(min = 1, message = "Password is required")))]
1183///     pub password: String,
1184/// }
1185/// ```
1186///
1187/// # Requirements
1188///
1189/// - The consumer crate's **native** build of `reinhardt` MUST enable the
1190///   `rest` feature — the macro expansion references
1191///   `::reinhardt::rest::openapi::Schema`.
1192/// - Applies only to `struct` items (named, tuple, or unit). Enums and unions
1193///   produce a compile error.
1194/// - Does not accept arguments in this version. Passing any tokens (e.g.
1195///   `#[dto(no_schema)]`) is a compile error.
1196/// - Unconditional `#[derive(Validate)]` or `#[derive(Schema)]` on the same
1197///   struct is a compile error. Both traits live behind the `native` cfg, so
1198///   an unconditional derive cannot resolve on wasm and would duplicate the
1199///   macro's emission on native. Either delete the derive (and let `#[dto]`
1200///   emit it) or wrap it in `#[cfg_attr(native, derive(...))]` yourself.
1201/// - Any pre-existing `#[cfg_attr(native, derive(Validate))]` or
1202///   `#[cfg_attr(native, derive(Schema))]` MUST be written *below* `#[dto]`,
1203///   not above it. Attribute proc macros only observe attributes that appear
1204///   under them in source order, so a `cfg_attr` placed above `#[dto]` is
1205///   invisible to the macro and would cause `#[dto]` to emit a duplicate
1206///   `cfg_attr(native, derive(...))` on native. Example of the supported
1207///   ordering:
1208///
1209/// ```rust,ignore
1210/// #[dto]
1211/// #[cfg_attr(native, derive(Schema))]
1212/// pub struct LoginRequest { /* ... */ }
1213/// ```
1214#[proc_macro_attribute]
1215pub fn dto(args: TokenStream, input: TokenStream) -> TokenStream {
1216	let input = parse_macro_input!(input as syn::DeriveInput);
1217
1218	dto::dto_impl(args.into(), input)
1219		.unwrap_or_else(|e| e.to_compile_error())
1220		.into()
1221}
1222
1223/// Settings attribute macro for composable configuration.
1224///
1225/// # Fragment mode
1226///
1227/// Marks a struct as a settings fragment:
1228///
1229/// ```rust,ignore
1230/// #[settings(fragment = true, section = "cache")]
1231/// pub struct CacheSettings {
1232///     pub backend: String,
1233/// }
1234/// ```
1235///
1236/// # Composition mode
1237///
1238/// Composes fragments into a project settings struct.
1239///
1240/// Supports two syntax forms:
1241/// - **Explicit**: `key: Type` — specify field name explicitly
1242/// - **Implicit**: `Type` — infer field name from type (requires `Settings` suffix)
1243///
1244/// Both forms can be mixed freely:
1245///
1246/// ```rust,ignore
1247/// // All implicit (XxxSettings → xxx)
1248/// #[settings(CoreSettings | CacheSettings | SessionSettings)]
1249/// pub struct ProjectSettings;
1250///
1251/// // Mixed implicit + explicit
1252/// #[settings(CoreSettings | CacheSettings | static_files: StaticSettings)]
1253/// pub struct ProjectSettings;
1254///
1255/// // Explicit only (original syntax, still fully supported)
1256/// #[settings(core: CoreSettings | cache: CacheSettings)]
1257/// pub struct ProjectSettings;
1258/// ```
1259///
1260/// Types without `Settings` suffix require explicit `key: Type` syntax. Note that
1261/// even for `*Settings` types, if the inferred field name would be a Rust keyword
1262/// (e.g. `StaticSettings` → `static`), you must use explicit `key: Type` syntax,
1263/// as in `static_files: StaticSettings` above.
1264#[proc_macro_attribute]
1265pub fn settings(args: TokenStream, input: TokenStream) -> TokenStream {
1266	let input_struct = parse_macro_input!(input as ItemStruct);
1267
1268	// Detect mode: if args contain "fragment", use fragment handler
1269	let args_str = args.to_string();
1270	if args_str.contains("fragment") {
1271		settings_fragment::settings_fragment_impl(args.into(), input_struct)
1272			.unwrap_or_else(|e| e.to_compile_error())
1273			.into()
1274	} else {
1275		settings_compose::settings_compose_impl(args.into(), input_struct)
1276			.unwrap_or_else(|e| e.to_compile_error())
1277			.into()
1278	}
1279}
1280
1281/// WebSocket consumer macro. Parallel to `#[get]` / `#[post]`.
1282///
1283/// Annotates an `async fn` that handles WebSocket messages (`on_message`).
1284/// Generates a `{FnName}Consumer` struct implementing `WebSocketConsumer`,
1285/// a factory function, inventory metadata, and URL resolver extension traits.
1286///
1287/// # Example
1288///
1289/// ```ignore
1290/// use reinhardt::websocket;
1291/// use reinhardt_websockets::consumers::{ConsumerContext, WebSocketResult};
1292/// use reinhardt_websockets::connection::Message;
1293///
1294/// #[websocket("/ws/chat/{room_id}/", name = "chat_ws")]
1295/// pub async fn chat_ws(
1296///     context: &mut ConsumerContext,
1297///     message: Message,
1298/// ) -> WebSocketResult<()> {
1299///     context.send_text("pong".to_string()).await
1300/// }
1301/// ```
1302#[proc_macro_attribute]
1303pub fn websocket(args: TokenStream, input: TokenStream) -> TokenStream {
1304	let input = parse_macro_input!(input as ItemFn);
1305	websocket::websocket_impl(args.into(), input)
1306		.unwrap_or_else(|e| e.to_compile_error())
1307		.into()
1308}
1309
1310/// Function-like proc macro for multi-file view modules.
1311///
1312/// When a view module uses per-file endpoint organization (one file per view in
1313/// `views/`), the URL resolver modules generated by `#[get]`/`#[post]`/etc.
1314/// live inside the submodules. This macro generates `pub use submod::*;`
1315/// for each `pub mod` declaration, bringing endpoint functions and their
1316/// resolver modules into the parent module scope.
1317///
1318/// This enables `#[url_patterns]` to discover resolvers using the standard
1319/// parent-module path convention (e.g., `.endpoint(views::login)`).
1320///
1321/// # Usage
1322///
1323/// ```rust,ignore
1324/// // views.rs (multi-file pattern)
1325/// use reinhardt::flatten_imports;
1326///
1327/// flatten_imports! {
1328///     pub mod login;
1329///     pub mod register;
1330/// }
1331///
1332/// // Generates:
1333/// //   pub mod login;
1334/// //   pub mod register;
1335/// //   pub use login::*;
1336/// //   pub use register::*;
1337/// ```
1338///
1339/// For single-file views where all functions are defined directly in
1340/// `views.rs`, this macro is not needed.
1341#[proc_macro]
1342pub fn flatten_imports(input: TokenStream) -> TokenStream {
1343	flatten_imports::flatten_imports_impl(input.into())
1344		.unwrap_or_else(|e| e.to_compile_error())
1345		.into()
1346}