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}