typestuff_macro/
lib.rs

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    // println!("{}", expanded);
24
25    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}