windjammer_ui_macro/
lib.rs1use proc_macro::TokenStream;
6use quote::quote;
7use syn::{parse_macro_input, Data, DeriveInput, Fields};
8
9#[proc_macro_attribute]
28pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream {
29 let input = parse_macro_input!(item as DeriveInput);
30
31 let name = &input.ident;
32 let generics = &input.generics;
33 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
34
35 let fields = match &input.data {
37 Data::Struct(data) => match &data.fields {
38 Fields::Named(fields) => &fields.named,
39 _ => {
40 return syn::Error::new_spanned(
41 &input,
42 "Component must be a struct with named fields",
43 )
44 .to_compile_error()
45 .into();
46 }
47 },
48 _ => {
49 return syn::Error::new_spanned(&input, "Component must be a struct")
50 .to_compile_error()
51 .into();
52 }
53 };
54
55 let field_names: Vec<_> = fields.iter().filter_map(|f| f.ident.as_ref()).collect();
57 let field_types: Vec<_> = fields.iter().map(|f| &f.ty).collect();
58
59 let expanded = quote! {
61 #[derive(Clone)]
62 #input
63
64 impl #impl_generics #name #ty_generics #where_clause {
65 pub fn new() -> Self {
67 Self {
68 #(#field_names: Default::default()),*
69 }
70 }
71
72 pub fn with_state(#(#field_names: #field_types),*) -> Self {
74 Self {
75 #(#field_names),*
76 }
77 }
78 }
79
80 impl #impl_generics Default for #name #ty_generics #where_clause {
81 fn default() -> Self {
82 Self::new()
83 }
84 }
85
86 unsafe impl #impl_generics Send for #name #ty_generics #where_clause {}
91 unsafe impl #impl_generics Sync for #name #ty_generics #where_clause {}
92 };
93
94 TokenStream::from(expanded)
95}
96
97#[proc_macro_derive(Props)]
109pub fn derive_props(input: TokenStream) -> TokenStream {
110 let input = parse_macro_input!(input as DeriveInput);
111 let name = &input.ident;
112 let generics = &input.generics;
113 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
114
115 let expanded = quote! {
116 impl #impl_generics windjammer_ui::component::ComponentProps for #name #ty_generics #where_clause {}
117 };
118
119 TokenStream::from(expanded)
120}
121
122#[cfg(test)]
123mod tests {
124 }