kit_macros/
lib.rs

1//! Procedural macros for the Kit framework
2//!
3//! This crate provides compile-time validated macros for:
4//! - Inertia.js responses with component validation
5//! - Named route redirects with route validation
6//! - Service auto-registration
7
8use proc_macro::TokenStream;
9
10mod domain_error;
11mod inertia;
12mod injectable;
13mod redirect;
14mod service;
15mod utils;
16
17/// Derive macro for generating `Serialize` implementation for Inertia props
18///
19/// # Example
20///
21/// ```rust,ignore
22/// #[derive(InertiaProps)]
23/// struct HomeProps {
24///     title: String,
25///     user: User,
26/// }
27/// ```
28#[proc_macro_derive(InertiaProps)]
29pub fn derive_inertia_props(input: TokenStream) -> TokenStream {
30    inertia::derive_inertia_props_impl(input)
31}
32
33/// Create an Inertia response with compile-time component validation
34///
35/// # Examples
36///
37/// ## With typed struct (recommended for type safety):
38/// ```rust,ignore
39/// #[derive(InertiaProps)]
40/// struct HomeProps {
41///     title: String,
42///     user: User,
43/// }
44///
45/// inertia_response!("Home", HomeProps { title: "Welcome".into(), user })
46/// ```
47///
48/// ## With JSON-like syntax (for quick prototyping):
49/// ```rust,ignore
50/// inertia_response!("Dashboard", { "user": { "name": "John" } })
51/// ```
52///
53/// This macro validates that the component file exists at compile time.
54/// If `frontend/src/pages/Dashboard.tsx` doesn't exist, you'll get a compile error.
55#[proc_macro]
56pub fn inertia_response(input: TokenStream) -> TokenStream {
57    inertia::inertia_response_impl(input)
58}
59
60/// Create a redirect to a named route with compile-time validation
61///
62/// # Examples
63///
64/// ```rust,ignore
65/// // Simple redirect
66/// redirect!("users.index").into()
67///
68/// // Redirect with route parameters
69/// redirect!("users.show").with("id", "42").into()
70///
71/// // Redirect with query parameters
72/// redirect!("users.index").query("page", "1").into()
73/// ```
74///
75/// This macro validates that the route name exists at compile time.
76/// If the route doesn't exist, you'll get a compile error with suggestions.
77#[proc_macro]
78pub fn redirect(input: TokenStream) -> TokenStream {
79    redirect::redirect_impl(input)
80}
81
82/// Mark a trait as a service for the App container
83///
84/// This attribute macro automatically adds `Send + Sync + 'static` bounds
85/// to your trait, making it suitable for use with the dependency injection
86/// container.
87///
88/// # Example
89///
90/// ```rust,ignore
91/// use kit::service;
92///
93/// #[service]
94/// pub trait HttpClient {
95///     async fn get(&self, url: &str) -> Result<String, Error>;
96/// }
97///
98/// // This expands to:
99/// pub trait HttpClient: Send + Sync + 'static {
100///     async fn get(&self, url: &str) -> Result<String, Error>;
101/// }
102/// ```
103///
104/// Then you can use it with the App container:
105///
106/// ```rust,ignore
107/// // Register
108/// App::bind::<dyn HttpClient>(Arc::new(RealHttpClient::new()));
109///
110/// // Resolve
111/// let client: Arc<dyn HttpClient> = App::make::<dyn HttpClient>().unwrap();
112/// ```
113#[proc_macro_attribute]
114pub fn service(attr: TokenStream, input: TokenStream) -> TokenStream {
115    service::service_impl(attr, input)
116}
117
118/// Attribute macro to auto-register a concrete type as a singleton
119///
120/// This macro automatically:
121/// 1. Derives `Default` and `Clone` for the struct
122/// 2. Registers it as a singleton in the App container at startup
123///
124/// # Example
125///
126/// ```rust,ignore
127/// use kit::injectable;
128///
129/// #[injectable]
130/// pub struct AppState {
131///     pub counter: u32,
132/// }
133///
134/// // Automatically registered at startup
135/// // Resolve via:
136/// let state: AppState = App::get().unwrap();
137/// ```
138#[proc_macro_attribute]
139pub fn injectable(_attr: TokenStream, input: TokenStream) -> TokenStream {
140    injectable::injectable_impl(input)
141}
142
143/// Define a domain error with automatic HTTP response conversion
144///
145/// This macro automatically:
146/// 1. Derives `Debug` and `Clone` for the type
147/// 2. Implements `Display`, `Error`, and `HttpError` traits
148/// 3. Implements `From<T> for FrameworkError` for seamless `?` usage
149///
150/// # Attributes
151///
152/// - `status`: HTTP status code (default: 500)
153/// - `message`: Error message for Display (default: struct name converted to sentence)
154///
155/// # Example
156///
157/// ```rust,ignore
158/// use kit::domain_error;
159///
160/// #[domain_error(status = 404, message = "User not found")]
161/// pub struct UserNotFoundError {
162///     pub user_id: i32,
163/// }
164///
165/// // Usage in controller - just use ? operator
166/// pub async fn get_user(id: i32) -> Result<User, FrameworkError> {
167///     users.find(id).ok_or(UserNotFoundError { user_id: id })?
168/// }
169/// ```
170#[proc_macro_attribute]
171pub fn domain_error(attr: TokenStream, input: TokenStream) -> TokenStream {
172    domain_error::domain_error_impl(attr, input)
173}