1use proc_macro::TokenStream;
11use quote::quote;
12use syn::{FnArg, ItemFn, Pat, ReturnType, Type, parse_macro_input};
13
14mod emit;
15mod parse;
16
17#[proc_macro]
29pub fn rsx(input: TokenStream) -> TokenStream {
30 let ast = parse_macro_input!(input as parse::RsxRoot);
31 emit::emit_rsx(ast).into()
32}
33
34#[proc_macro_attribute]
53pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream {
54 let input = parse_macro_input!(item as ItemFn);
55
56 let name = &input.sig.ident;
57 let vis = &input.vis;
58 let body = &input.block;
59
60 let return_ty = match &input.sig.output {
61 ReturnType::Type(_, ty) => ty,
62 _ => {
63 return syn::Error::new_spanned(&input.sig, "component must return View")
64 .to_compile_error()
65 .into();
66 }
67 };
68
69 let mut inputs = input.sig.inputs.iter();
70
71 let cx = match inputs.next() {
73 Some(FnArg::Typed(pat)) => pat,
74 _ => {
75 return syn::Error::new_spanned(&input.sig, "first argument must be cx: &Arc<Context>")
76 .to_compile_error()
77 .into();
78 }
79 };
80
81 let cx_ident = match &*cx.pat {
82 Pat::Ident(id) => &id.ident,
83 _ => unreachable!(),
84 };
85
86 let cx_ty = &cx.ty;
87
88 let mut struct_fields = Vec::new();
90 let mut render_params = Vec::new();
91 let mut call_args = Vec::new();
92
93 for arg in inputs {
94 let FnArg::Typed(pat) = arg else { continue };
95
96 let ident = match &*pat.pat {
97 Pat::Ident(id) => &id.ident,
98 _ => {
99 return syn::Error::new_spanned(pat, "unsupported prop pattern")
100 .to_compile_error()
101 .into();
102 }
103 };
104
105 let owned_ty = match &*pat.ty {
107 Type::Reference(r) => &r.elem,
108 ty => ty,
109 };
110
111 struct_fields.push(quote! {
112 pub #ident: #owned_ty
113 });
114
115 render_params.push(quote! {
116 #ident: &#owned_ty
117 });
118
119 call_args.push(quote! {
120 &self.#ident
121 });
122 }
123
124 let expanded = quote! {
125 #vis struct #name {
126 #(#struct_fields,)*
127 }
128
129 impl #name {
130 pub fn component(
131 #cx_ident: #cx_ty,
132 #(#render_params,)*
133 ) -> #return_ty {
134 #body
135 }
136 }
137
138 impl ComponentImpl for #name {
139 fn call(&self, cx: &std::sync::Arc<Context>) -> #return_ty {
140 Self::component(
141 cx,
142 #(#call_args,)*
143 )
144 }
145 }
146 };
147
148 expanded.into()
149}