resuma_macros/lib.rs
1//! Resuma procedural macros.
2//!
3//! These macros are the surface area Resuma exposes to user code:
4//!
5//! * [`view!`] — JSX-like template syntax that builds a `View` tree.
6//! * [`#[component]`] — turns a function into a Resuma component.
7//! * [`#[server]`] — exposes an async fn as a server action / RPC.
8//! * [`#[island]`] — marks an interactive island (its handlers ship to JS).
9//! * [`js!`] — escape hatch for raw JavaScript handler bodies.
10//!
11//! The interesting one is `view!`: it walks the JSX-ish tokens, recognises
12//! `onClick={...}` style attributes, and feeds the closure body to
13//! `resuma-rs2js` to produce a JS chunk. The Rust side only stores a
14//! `HandlerRef` pointing at that chunk so SSR can emit `data-r-on:click=…`.
15
16mod component_macro;
17mod island_macro;
18mod js_macro;
19mod layout_macro;
20mod load_macro;
21mod middleware_macro;
22mod rs2js;
23mod server_macro;
24mod submit_macro;
25mod view_macro;
26
27use proc_macro::TokenStream;
28
29/// `view!` — JSX-like template macro.
30///
31/// ```ignore
32/// view! {
33/// <div class="card">
34/// <h1>"Hello " {name}</h1>
35/// <button onClick={move |_| count.update(|c| *c += 1)}>"+"</button>
36/// </div>
37/// }
38/// ```
39#[proc_macro]
40pub fn view(input: TokenStream) -> TokenStream {
41 view_macro::expand(input.into()).into()
42}
43
44/// `#[component]` — registers a function as a Resuma component.
45///
46/// ```ignore
47/// #[component]
48/// fn Greeting(name: String) -> View {
49/// view! { <h1>"Hello "{name}</h1> }
50/// }
51/// ```
52#[proc_macro_attribute]
53pub fn component(args: TokenStream, input: TokenStream) -> TokenStream {
54 component_macro::expand(args.into(), input.into()).into()
55}
56
57/// `#[server]` — exposes an async fn as a server action callable from
58/// `actions::name(...)` in the browser.
59#[proc_macro_attribute]
60pub fn server(args: TokenStream, input: TokenStream) -> TokenStream {
61 server_macro::expand(args.into(), input.into()).into()
62}
63
64/// `#[island]` — marks a component as an interactive island. Its event
65/// handlers are extracted and shipped to the client as a single chunk.
66#[proc_macro_attribute]
67pub fn island(args: TokenStream, input: TokenStream) -> TokenStream {
68 island_macro::expand(args.into(), input.into()).into()
69}
70
71/// `#[load]` — Resuma Flow server data loader (runs before page render).
72#[proc_macro_attribute]
73pub fn load(args: TokenStream, input: TokenStream) -> TokenStream {
74 load_macro::expand(args.into(), input.into()).into()
75}
76
77/// `#[submit]` — Resuma Flow form submission handler.
78#[proc_macro_attribute]
79pub fn submit(args: TokenStream, input: TokenStream) -> TokenStream {
80 submit_macro::expand(args.into(), input.into()).into()
81}
82
83/// `#[layout]` — Resuma Flow layout wrapper for nested pages.
84#[proc_macro_attribute]
85pub fn layout(args: TokenStream, input: TokenStream) -> TokenStream {
86 layout_macro::expand(args.into(), input.into()).into()
87}
88
89/// `#[middleware]` — Resuma Flow request middleware.
90#[proc_macro_attribute]
91pub fn middleware(args: TokenStream, input: TokenStream) -> TokenStream {
92 middleware_macro::expand(args.into(), input.into()).into()
93}
94
95/// `js!` — raw JavaScript escape hatch for event handlers.
96///
97/// ```ignore
98/// onClick={ js! { state.count.set(state.count.value + 1); } }
99/// ```
100#[proc_macro]
101pub fn js(input: TokenStream) -> TokenStream {
102 js_macro::expand(input.into()).into()
103}