1use proc_macro::TokenStream;
2use proc_macro2::{Ident, TokenStream as TokenStream2};
3use quote::{format_ident, quote, quote_spanned};
4use syn::{parse_macro_input, spanned::Spanned, DeriveInput, ItemStruct, LitStr, Type};
5
6#[proc_macro_attribute]
7pub fn transformable(attr: TokenStream, input: TokenStream) -> TokenStream {
8 let attr = TokenStream2::from(attr);
9 let input = TokenStream2::from(input);
10 let span = input.span();
11
12 let expanded = match transformable_impl(attr, input) {
13 Ok(ts) => ts.into(),
14 Err(e) => {
15 let expanded = quote_spanned! { span =>
16 compile_error!(#e);
17 };
18
19 expanded.into()
20 }
21 };
22
23 expanded
26}
27
28fn transformable_impl(_attr: TokenStream2, input: TokenStream2) -> Result<TokenStream2, String> {
29 let input = match syn::parse2::<ItemStruct>(input) {
30 Ok(input) => input,
31 Err(e) => return Ok(e.into_compile_error().into()),
32 };
33
34 let vis = input.vis;
35 let ident = input.ident;
36
37 let fields = match input.fields {
38 syn::Fields::Named(fields) => fields.named,
39 _ => return Err("Only named fields are supported".into()),
40 };
41
42 let fields_mod_ident = format_ident!("{}_fields", ident);
43
44 let field_structs = {
45 let fields = fields.iter().map(|f| {
46 let field_ident = f.ident.as_ref().unwrap();
47 let vis = match vis {
48 syn::Visibility::Public(_) => quote! {pub},
49 syn::Visibility::Restricted(_) | syn::Visibility::Crate(_) => quote! {pub(crate)},
50 syn::Visibility::Inherited => quote! {pub(in super)},
51 };
52 let name_lit = LitStr::new(&field_ident.to_string(), field_ident.span());
53 quote! {
54 #[allow(non_camel_case_types)]
55 #vis struct #field_ident;
56 impl ::typestuff::transformable::Field for #field_ident {
57 type Object = super::#ident<()>;
58
59 fn get() -> Self
60 where
61 Self: Sized
62 {
63 #field_ident
64 }
65
66 fn name(&self) -> &'static str {
67 #name_lit
68 }
69 }
70 }
71 });
72 quote! {
73 #[allow(non_snake_case)]
74 #vis mod #fields_mod_ident {
75 #(#fields)*
76 }
77 }
78 };
79
80 let expanded = {
81 let attrs = input.attrs;
82 let kstruct = input.struct_token;
83
84 let fields_const = fields.iter().map(|f| f.ident.as_ref().unwrap().clone());
85
86 let fields = fields.iter().map(|f| {
87 let mut f = f.clone();
88 let ty = f.ty;
89 let field_name = f.ident.as_ref().unwrap();
90 f.ty = Type::Verbatim(quote! {
91 <T as ::typestuff::transformable::Transformation>::Map<#fields_mod_ident :: #field_name, #ty>,
92 });
93 f
94 });
95
96 quote! {
97 #field_structs
98
99 #(#attrs)*
100 #vis #kstruct #ident <T=()> where T: ::typestuff::transformable::Transformation {
101 #(
102 #fields
103 )*
104 }
105
106 impl ::typestuff::transformable::Transformable for #ident<()> {
107 const FIELDS: &'static [&'static dyn ::typestuff::transformable::Field<Object=Self>] = &[
108 #(&#fields_mod_ident::#fields_const),*
109 ];
110 }
111 }
112 };
113
114 Ok(expanded)
115}
116
117#[proc_macro_derive(TypeFn)]
118pub fn derive_type_fn(input: TokenStream) -> TokenStream {
119 let input = parse_macro_input!(input as DeriveInput);
120
121 struct TypeFnFrame {
122 marker_name: Ident,
123 result_name: Ident,
124 types: Vec<Ident>,
125 }
126
127 let mut type_acc = input
128 .generics
129 .type_params()
130 .map(|param| param.ident.clone())
131 .collect::<Vec<_>>();
132 type_acc
133 .pop()
134 .expect("At least one type parameter required");
135
136 let mut next_result_name = input.ident.clone();
137 let frames = (0..=(type_acc.len()))
138 .map(|i| {
139 let marker_name = format_ident!("{}{}", input.ident, "_".repeat(i + 1));
140
141 let result_name = std::mem::replace(&mut next_result_name, marker_name.clone());
142
143 let types = type_acc.clone();
144 type_acc.pop();
145
146 TypeFnFrame {
147 marker_name,
148 result_name,
149 types,
150 }
151 })
152 .map(
153 |TypeFnFrame {
154 marker_name,
155 result_name,
156 types,
157 }| {
158 quote! {
159 pub struct #marker_name<#(#types),*> {
160 _phantom: ::std::marker::PhantomData<(#(*const #types,)*)>
161 }
162
163 impl<#(#types),*> ::typestuff::type_fn::TypeFn for #marker_name<#(#types),*> {
164 type Apply<A> = #result_name<#(#types),* A>;
165 }
166
167 impl<#(#types,)* A> ::typestuff::type_fn::TypeMatch for #result_name<#(#types,)* A> {
168 type Constructor = #marker_name<#(#types,)*>;
169 type Argument = A;
170 }
171 }
172 },
173 );
174
175 let expanded = quote! {#(#frames)*};
176
177 println!("{}", expanded);
178
179 expanded.into()
180}