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 inertia;
11mod injectable;
12mod redirect;
13mod service;
14mod utils;
15
16/// Derive macro for generating `Serialize` implementation for Inertia props
17///
18/// # Example
19///
20/// ```rust,ignore
21/// #[derive(InertiaProps)]
22/// struct HomeProps {
23///     title: String,
24///     user: User,
25/// }
26/// ```
27#[proc_macro_derive(InertiaProps)]
28pub fn derive_inertia_props(input: TokenStream) -> TokenStream {
29    inertia::derive_inertia_props_impl(input)
30}
31
32/// Create an Inertia response with compile-time component validation
33///
34/// # Examples
35///
36/// ## With typed struct (recommended for type safety):
37/// ```rust,ignore
38/// #[derive(InertiaProps)]
39/// struct HomeProps {
40///     title: String,
41///     user: User,
42/// }
43///
44/// inertia_response!("Home", HomeProps { title: "Welcome".into(), user })
45/// ```
46///
47/// ## With JSON-like syntax (for quick prototyping):
48/// ```rust,ignore
49/// inertia_response!("Dashboard", { "user": { "name": "John" } })
50/// ```
51///
52/// This macro validates that the component file exists at compile time.
53/// If `frontend/src/pages/Dashboard.tsx` doesn't exist, you'll get a compile error.
54#[proc_macro]
55pub fn inertia_response(input: TokenStream) -> TokenStream {
56    inertia::inertia_response_impl(input)
57}
58
59/// Create a redirect to a named route with compile-time validation
60///
61/// # Examples
62///
63/// ```rust,ignore
64/// // Simple redirect
65/// redirect!("users.index").into()
66///
67/// // Redirect with route parameters
68/// redirect!("users.show").with("id", "42").into()
69///
70/// // Redirect with query parameters
71/// redirect!("users.index").query("page", "1").into()
72/// ```
73///
74/// This macro validates that the route name exists at compile time.
75/// If the route doesn't exist, you'll get a compile error with suggestions.
76#[proc_macro]
77pub fn redirect(input: TokenStream) -> TokenStream {
78    redirect::redirect_impl(input)
79}
80
81/// Mark a trait as a service for the App container
82///
83/// This attribute macro automatically adds `Send + Sync + 'static` bounds
84/// to your trait, making it suitable for use with the dependency injection
85/// container.
86///
87/// # Example
88///
89/// ```rust,ignore
90/// use kit::service;
91///
92/// #[service]
93/// pub trait HttpClient {
94///     async fn get(&self, url: &str) -> Result<String, Error>;
95/// }
96///
97/// // This expands to:
98/// pub trait HttpClient: Send + Sync + 'static {
99///     async fn get(&self, url: &str) -> Result<String, Error>;
100/// }
101/// ```
102///
103/// Then you can use it with the App container:
104///
105/// ```rust,ignore
106/// // Register
107/// App::bind::<dyn HttpClient>(Arc::new(RealHttpClient::new()));
108///
109/// // Resolve
110/// let client: Arc<dyn HttpClient> = App::make::<dyn HttpClient>().unwrap();
111/// ```
112#[proc_macro_attribute]
113pub fn service(attr: TokenStream, input: TokenStream) -> TokenStream {
114    service::service_impl(attr, input)
115}
116
117/// Attribute macro to auto-register a concrete type as a singleton
118///
119/// This macro automatically:
120/// 1. Derives `Default` and `Clone` for the struct
121/// 2. Registers it as a singleton in the App container at startup
122///
123/// # Example
124///
125/// ```rust,ignore
126/// use kit::injectable;
127///
128/// #[injectable]
129/// pub struct AppState {
130///     pub counter: u32,
131/// }
132///
133/// // Automatically registered at startup
134/// // Resolve via:
135/// let state: AppState = App::get().unwrap();
136/// ```
137#[proc_macro_attribute]
138pub fn injectable(_attr: TokenStream, input: TokenStream) -> TokenStream {
139    injectable::injectable_impl(input)
140}