fallback_derive/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::Ident;
3use quote::quote;
4use syn::{
5    parse::{Parse, Parser},
6    parse_macro_input, parse_str, Data, DeriveInput, Expr, FieldValue, Fields, Type,
7};
8
9#[proc_macro_derive(FallbackSpec)]
10pub fn derive_fallback_spec(input: TokenStream) -> TokenStream {
11    let struct_input = parse_macro_input!(input as DeriveInput);
12    let struct_name = struct_input.ident;
13    let vis = struct_input.vis;
14    let (fallback_data_declare, data_idents, base_data_idents, some_exact, none_exact, construct) =
15        match struct_input.data {
16            Data::Struct(data) => match data.fields {
17                Fields::Named(fields) => {
18                    let fields = fields.named.into_iter().collect::<Vec<_>>();
19                    let fallback_data_declare = fields
20                        .iter()
21                        .map(|field| {
22                            let mut field = field.clone();
23                            let ty = field.ty.clone();
24                            field.ty = Type::parse
25                                .parse2(quote! {::fallback::Fallback<#ty>})
26                                .unwrap();
27                            field
28                        })
29                        .collect::<Vec<_>>();
30                    let data_idents = fields
31                        .iter()
32                        .map(|field| field.ident.clone().unwrap())
33                        .collect::<Vec<_>>();
34                    let base_data_idents = fields
35                        .iter()
36                        .map(|field| {
37                            parse_str::<Ident>(&format!("base_{}", field.ident.clone().unwrap()))
38                                .expect("Parse base idents failed")
39                        })
40                        .collect::<Vec<_>>();
41                    let some_exact = fields
42                        .iter()
43                        .map(|field| {
44                            parse_str::<Expr>(&format!(
45                                "Some(data.{})",
46                                field.ident.clone().unwrap()
47                            ))
48                            .expect("Parse some exact failed")
49                        })
50                        .collect::<Vec<_>>();
51                    let none_exact =
52                        std::iter::repeat(parse_str::<Expr>("None").expect("Parse None failed"))
53                            .take(fields.len())
54                            .collect::<Vec<_>>();
55                    let construct = fields
56                        .iter()
57                        .map(|field| {
58                            let ident = field.ident.clone().unwrap();
59                            let base_ident =
60                                Ident::new(&format!("base_{}", ident.clone()), ident.span());
61                            FieldValue::parse
62                                .parse2(
63                                    quote! {#ident: ::fallback::Fallback::new(#ident, #base_ident)},
64                                )
65                                .expect("Parse field value failed")
66                        })
67                        .collect::<Vec<_>>();
68                    (
69                        fallback_data_declare,
70                        data_idents,
71                        base_data_idents,
72                        some_exact,
73                        none_exact,
74                        construct,
75                    )
76                }
77                _ => unimplemented!(),
78            },
79            _ => unimplemented!(),
80        };
81    let fallback_struct_name = parse_str::<Ident>(&format!("__Fallback{}", struct_name))
82        .expect("Parse fallback name failed");
83    let output = quote! {
84        #[doc(hidden)]
85        #vis struct #fallback_struct_name {
86            #(#fallback_data_declare ,)*
87        }
88
89        impl FallbackSpec for #struct_name {
90            type SpecType = #fallback_struct_name;
91        }
92
93        impl From<::fallback::Fallback<#struct_name>> for #fallback_struct_name {
94            fn from(data: ::fallback::Fallback<#struct_name>) -> Self {
95                let (data, base_data) = data.unzip();
96                let (#(#data_idents ,)*) = match data {
97                    Some(data) => (#(#some_exact ,)*),
98                    None => (#(#none_exact ,)*),
99                };
100                let (#(#base_data_idents ,)*) = match base_data {
101                    Some(data) => (#(#some_exact ,)*),
102                    None => (#(#none_exact ,)*),
103                };
104                Self {
105                    #(#construct ,)*
106                }
107            }
108        }
109    };
110    TokenStream::from(output)
111}