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