Skip to main content

next_rs_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, ItemFn};
4
5/// Marks a function as a server component.
6///
7/// Server components run only on the server and can access databases,
8/// file systems, and other server-only resources directly.
9///
10/// ```rust,ignore
11/// #[server_component]
12/// fn article_list() -> Element {
13///     div().child(h1().text("Articles"))
14/// }
15/// ```
16#[proc_macro_attribute]
17pub fn server_component(_attr: TokenStream, item: TokenStream) -> TokenStream {
18    let input = parse_macro_input!(item as ItemFn);
19    let fn_name = &input.sig.ident;
20    let fn_name_str = fn_name.to_string();
21    let vis = &input.vis;
22    let block = &input.block;
23    let output = &input.sig.output;
24
25    let expanded = quote! {
26        #vis fn #fn_name() #output {
27            next_rs_rsc::global_registry().register_server(module_path!(), #fn_name_str);
28            (|| #block)()
29        }
30    };
31
32    expanded.into()
33}
34
35/// Marks a function as a client component.
36///
37/// Client components are shipped to the browser as WASM and can use
38/// interactive features like event handlers and reactive state.
39///
40/// ```rust,ignore
41/// #[client_component]
42/// fn counter() -> Element {
43///     let (count, set_count) = create_signal(0);
44///     div().child(button().text("+").on_click(move |_| set_count.update(|n| *n += 1)))
45/// }
46/// ```
47#[proc_macro_attribute]
48pub fn client_component(_attr: TokenStream, item: TokenStream) -> TokenStream {
49    let input = parse_macro_input!(item as ItemFn);
50    let fn_name = &input.sig.ident;
51    let fn_name_str = fn_name.to_string();
52    let vis = &input.vis;
53    let block = &input.block;
54    let output = &input.sig.output;
55
56    let expanded = quote! {
57        #vis fn #fn_name() #output {
58            next_rs_rsc::global_registry().register_client(module_path!(), #fn_name_str);
59            (|| #block)()
60        }
61    };
62
63    expanded.into()
64}
65
66/// Marks an async function as a server action.
67///
68/// Server actions can be called from client components and are
69/// automatically serialized/deserialized across the network boundary.
70///
71/// ```rust,ignore
72/// #[server_action]
73/// async fn create_todo(title: String) -> Result<Todo, ActionError> {
74///     db::insert_todo(&title).await
75/// }
76/// ```
77#[proc_macro_attribute]
78pub fn server_action(_attr: TokenStream, item: TokenStream) -> TokenStream {
79    let input = parse_macro_input!(item as ItemFn);
80    let fn_name = &input.sig.ident;
81    let fn_name_str = fn_name.to_string();
82    let vis = &input.vis;
83    let sig = &input.sig;
84    let block = &input.block;
85
86    let expanded = quote! {
87        #vis #sig {
88            next_rs_rsc::global_registry().register_server(module_path!(), #fn_name_str);
89            #block
90        }
91    };
92
93    expanded.into()
94}