1use proc_macro2::{Span, TokenStream};
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput, Fields};
4
5#[proc_macro_derive(Lens)]
6pub fn my_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
7 let input = parse_macro_input!(input as DeriveInput);
8
9 let lensed_ident = input.ident;
10 let lens_vis = input.vis;
11 let generics = input.generics;
12 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
13
14 let (inner_impls, outer_impls) = match input.data {
15 syn::Data::Struct(s) => match s.fields {
16 Fields::Named(fields) => fields
17 .named
18 .into_iter()
19 .map(|field| (field.ident.unwrap(), field.ty))
20 .map(|(ident, ty)| {
21 let outer = quote! {
22 #[derive(Copy, Clone)]
23 #[allow(non_camel_case_types)]
24 #lens_vis struct #ident;
25 impl ::ui4::lens::Lens for #ident {
26 type In = #lensed_ident;
27 type Out = #ty;
28
29 fn get<'a>(&self, v: &'a #lensed_ident) -> &'a #ty {
30 &v.#ident
31 }
32
33 fn get_mut<'a>(&self, v: &'a mut #lensed_ident) -> &'a mut #ty {
34 &mut v.#ident
35 }
36 }
37 };
38 let inner = quote! {
39 #[allow(non_upper_case_globals)]
40 #lens_vis const #ident: #ident = #ident;
41 };
42 (inner, outer)
43 })
44 .unzip::<_, _, TokenStream, TokenStream>(),
45 Fields::Unnamed(fields) => {
46 let lens_name =
47 syn::Ident::new(&format!("{}Lens", lensed_ident), Span::call_site());
48 let (inner_impls, outer_impls) = fields
49 .unnamed
50 .into_iter()
51 .map(|field| field.ty)
52 .enumerate()
53 .map(|(i, ty)| {
54 let index = syn::Index::from(i);
55 let outer = quote! {
56 impl ::ui4::lens::Lens for #lens_name<#i> {
57 type In = #lensed_ident;
58 type Out = #ty;
59
60 fn get<'a>(&self, v: &'a #lensed_ident) -> &'a #ty {
61 &v.#index
62 }
63
64 fn get_mut<'a>(&self, v: &'a mut #lensed_ident) -> &'a mut #ty {
65 &mut v.#index
66 }
67 }
68 };
69 let n_ident = syn::Ident::new(&format!("F{}", i), Span::call_site());
70 let inner = quote! {
71 #lens_vis const #n_ident: #lens_name::<#i> = #lens_name::<#i>;
72 };
73 (inner, outer)
74 })
75 .unzip::<_, _, TokenStream, TokenStream>();
76 let outer = quote! {
77 #[derive(Copy, Clone)]
78 #lens_vis struct #lens_name<const N: usize>;
79 #outer_impls
80 };
81 let inner = quote! {
82 #inner_impls
83 };
84 (inner, outer)
85 }
86 Fields::Unit => unimplemented!("Unit structs not supported"),
87 },
88 _ => unimplemented!("Only structs are supported"),
89 };
90
91 let expanded = quote! {
93 impl #impl_generics #lensed_ident #ty_generics #where_clause {
94 #inner_impls
95 }
96
97 #outer_impls
98 };
99
100 expanded.into()
102}