1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote, ToTokens};
5use syn::{Data, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, FnArg, ItemFn, Lit, Meta, MetaNameValue, parse_macro_input, parse_quote, Path, PatType, Signature, Variant};
6use syn::__private::TokenStream2;
7use syn::punctuated::Punctuated;
8use syn::token::Comma;
9
10
11#[proc_macro_attribute]
70pub fn export(_attr: TokenStream, input: TokenStream) -> TokenStream {
71 input
72}
73
74
75#[proc_macro_attribute]
76pub fn register(_attr: TokenStream, input: TokenStream) -> TokenStream {
77 let input = TokenStream2::from(input);
78 let expanded = quote! {
79 #[repr(C)]
80 #input
81 };
82 TokenStream::from(expanded)
83}
84
85#[proc_macro_attribute]
86pub fn opaque(_attr: TokenStream, input: TokenStream) -> TokenStream {
87 input
93 }
95
96
97#[proc_macro_derive(CompositionContext)]
98pub fn composition_context_derive(input: TokenStream) -> TokenStream {
99 let input = parse_macro_input!(input as DeriveInput);
100
101 let name = &input.ident;
102 let expanded = quote!(impl crate::composable::CompositionContext for #name {});
103
104 TokenStream::from(expanded)
105}
106
107#[proc_macro_derive(MethodCall, attributes(namespace))]
108pub fn method_call_derive(input: TokenStream) -> TokenStream {
109 let input = parse_macro_input!(input as DeriveInput);
110 let name = input.ident;
111 let namespace = input.attrs.iter()
112 .find(|attr| attr.path.is_ident("namespace"))
113 .and_then(|attr| match attr.parse_meta() {
114 Ok(Meta::NameValue(MetaNameValue { lit: Lit::Str(lit), .. })) => Some(lit.parse::<Path>().expect("Invalid namespace")),
115 _ => None
116 })
117 .expect("namespace attribute is required");
118
119 let expression_enum_name = format_ident!("{}Expr", name);
120 let mut expression_variants = Punctuated::<TokenStream2, Comma>::new();
121 let mut methods = Punctuated::<TokenStream2, Comma>::new();
122 let mut exprs = Punctuated::<TokenStream2, Comma>::new();
123
124 if let Data::Enum(data) = &input.data {
125 for Variant { ident, fields, .. } in &data.variants {
126 match fields {
127 Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
128 let field_count = unnamed.len();
129 let field_names = (0..field_count).map(|i| format_ident!("field{}", i)).collect::<Vec<_>>();
130 let field_types = unnamed.iter().map(|f| &f.ty).collect::<Vec<_>>();
131 expression_variants.push(quote!(#ident(#(#field_types,)* syn::__private::TokenStream2)));
132 methods.push(quote!(#expression_enum_name::#ident(#(#field_names,)* _) => #name::#ident(#(#field_names.clone(),)*).to_token_stream()));
133 exprs.push(quote!(#expression_enum_name::#ident(#(#field_names,)* expr) => expr));
134 },
135 Fields::Named(FieldsNamed { named, .. }) => {
136 let field_names = named.iter().map(|f| f.ident.clone().unwrap()).collect::<Vec<_>>();
137 let field_types = named.iter().map(|f| &f.ty).collect::<Vec<_>>();
138 expression_variants.push(quote!(#ident { #(#field_names: #field_types,)* expr: syn::__private::TokenStream2 }));
139 methods.push(quote!(#expression_enum_name::#ident { #(#field_names,)* .. } => #name::#ident { #(#field_names: #field_names.clone(),)* }.to_token_stream()));
140 exprs.push(quote!(#expression_enum_name::#ident { #(#field_names,)* expr } => expr));
141 },
142 Fields::Unit => {
143 expression_variants.push(quote!(#ident(syn::__private::TokenStream2)));
144 methods.push(quote!(#expression_enum_name::#ident(_) => #name::#ident.to_token_stream()));
145 exprs.push(quote!(#expression_enum_name::#ident(expr) => expr));
146 }
147 }
148 }
149 }
150
151 let expanded = quote! {
152 #[derive(Clone, Debug)]
153 pub enum #expression_enum_name {
154 #expression_variants
155 }
156 impl crate::presentation::MethodCall for #expression_enum_name {
157 fn method(&self) -> TokenStream2 {
158 let mut tokens = TokenStream2::new();
159 let method = match self {
160 #methods
161 };
162 let ns = syn::punctuated::Punctuated::<_, syn::token::Colon2>::from_iter([quote!(#namespace), method]);
163 tokens.append_all(vec![ns.to_token_stream()]);
164 tokens
165 }
166 fn expr(&self) -> &TokenStream2 {
167 match self {
168 #exprs
169 }
170 }
171 }
172 impl ToTokens for #expression_enum_name {
173 fn to_tokens(&self, dst: &mut TokenStream2) {
174 (self as &dyn crate::presentation::MethodCall).to_tokens(dst)
175 }
176 }
177 };
178 TokenStream::from(expanded)
179}
180
181#[proc_macro_derive(Display)]
182pub fn to_string_derive(input: TokenStream) -> TokenStream {
183 let input = parse_macro_input!(input as DeriveInput);
184 let name = input.ident;
185 let data = match input.data {
186 Data::Enum(data) => data,
187 _ => panic!("#[derive(ToString)] is only defined for enums"),
188 };
189 let match_arms = data.variants.iter().map(|Variant { ident, fields, .. } | {
190 match fields {
191 Fields::Named(fields) => quote! { Self::#ident { .. } => format!("{}{}", stringify!(#ident), stringify!(#fields)), },
192 Fields::Unnamed(fields) => quote! { Self::#ident(..) => format!("{}{}", stringify!(#ident), stringify!(#fields)), },
193 Fields::Unit => quote! { Self::#ident => format!("{}", stringify!(#ident)), }
194 }
195 });
201
202 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
203 let expanded = quote! {
206 impl #impl_generics std::fmt::Display for #name #ty_generics #where_clause {
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 f.write_str(match self {
209 #(#match_arms)*
210 }.as_str())
211 }
212 }
213 };
214 TokenStream::from(expanded)
215}
216
217#[proc_macro_derive(BasicComposerOwner)]
218pub fn basic_composer_owner_derive(input: TokenStream) -> TokenStream {
219 let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput);
220 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
221 let expanded = quote! {
222 impl #impl_generics crate::composer::BasicComposerOwner<crate::presentable::Context, LANG, SPEC, Gen> for #ident #ty_generics #where_clause {
223 fn base(&self) -> &crate::composer::BasicComposer<crate::composer::ParentComposer<Self>, LANG> {
224 &self.base
225 }
226 }
227 };
228 TokenStream::from(expanded)
229}
230#[proc_macro_derive(ComposerBase)]
231pub fn composer_base_derive(input: TokenStream) -> TokenStream {
232 let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput);
233 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
234 let expanded = quote! {
235 impl #impl_generics crate::composer::BasicComposerOwner<LANG, SPEC> for #ident #ty_generics #where_clause {
236 fn base(&self) -> &crate::composer::BasicComposerLink<LANG, SPEC, Self> {
237 &self.base
238 }
239 }
240 impl #impl_generics crate::composer::AttrComposable<SPEC::Attr> for #ident #ty_generics #where_clause {
241 fn compose_attributes(&self) -> SPEC::Attr {
242 self.base().compose_attributes()
243 }
244 }
245 impl #impl_generics crate::composer::GenericsComposable<SPEC::Gen> for #ident #ty_generics #where_clause {
246 fn compose_generics(&self) -> SPEC::Gen {
247 self.base().compose_generics()
248 }
249 }
250 impl #impl_generics crate::composer::LifetimesComposable<SPEC::Lt> for #ident #ty_generics #where_clause {
251 fn compose_lifetimes(&self) -> SPEC::Lt {
252 self.base().compose_lifetimes()
253 }
254 }
255 impl #impl_generics crate::composer::SourceAccessible for #ident #ty_generics #where_clause {
256 fn context(&self) -> &ComposerLink<crate::context::ScopeContext> {
257 self.base().context()
258 }
259 }
260 impl #impl_generics crate::composer::TypeAspect<SPEC::TYC> for #ident #ty_generics #where_clause {
261 fn type_context_ref(&self) -> &SPEC::TYC {
262 self.base().type_context_ref()
263 }
264 }
265 };
266 TokenStream::from(expanded)
267}
268
269#[proc_macro_attribute]
270pub fn debug_io(_attr: TokenStream, item: TokenStream) -> TokenStream {
271 let input = parse_macro_input!(item as ItemFn);
272 let ItemFn { sig: Signature { ident: ref method_name, ref inputs, .. }, ref block, .. } = input;
273 let args: Vec<_> = inputs.iter().filter_map(|arg| {
274 if let FnArg::Typed(PatType { pat, .. }) = arg {
275 Some(quote! { #pat })
276 } else {
277 None
278 }
279 }).collect();
280 let fn_name = format!("{}", method_name);
281
282 let args_str = args.iter().map(ToTokens::to_token_stream).collect::<Vec<_>>();
283 let new_block = quote! {{
284 let debug_str = #fn_name;
285 let result = {
286 #block
287 };
288 println!("{}({:?}) -> {:?}", debug_str, #(#args_str),*, result);
289 result
290 }};
291 let mut output = input.clone();
292
293 output.block = parse_quote!(#new_block);
294 TokenStream::from(quote! { #output })
295}
296
297
298
299