1extern crate proc_macro;
7
8use proc_macro::TokenStream;
9use quote::{quote, ToTokens};
10use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields};
11
12#[proc_macro_derive(HListSupport)]
13pub fn hlist_support_derive(input: TokenStream) -> TokenStream {
14 let input = parse_macro_input!(input as DeriveInput);
16
17 let data_struct: DataStruct;
19 if let Data::Struct(s) = input.data {
20 data_struct = s
21 } else {
22 panic!("`HListSupport` may only be applied to structs")
23 }
24
25 let fields: Fields;
28 if let Fields::Named(_) = data_struct.fields {
29 fields = data_struct.fields
30 } else {
31 panic!("`HListSupport` may only be applied to structs with named fields")
32 }
33
34 let struct_name = &input.ident;
36
37 let hlist_type = hlist_type(fields.iter());
39
40 let hlist_pat = hlist_pattern(fields.iter());
42
43 let struct_field_init = struct_field_init(fields.iter());
45
46 let hlist_cloned_init = hlist_cloned_init(fields.iter());
48
49 let hlist_init = hlist_init(fields.iter());
51
52 let expanded = quote! {
54 #[allow(dead_code)]
56 impl FromHList<#hlist_type> for #struct_name {
57 fn from_hlist(hlist: #hlist_type) -> Self {
58 match hlist {
59 #hlist_pat => #struct_name { #struct_field_init }
60 }
61 }
62 }
63
64 #[allow(dead_code)]
66 impl ToHList<#hlist_type> for #struct_name {
67 fn to_hlist(&self) -> #hlist_type {
68 #hlist_cloned_init
69 }
70 }
71
72 #[allow(dead_code)]
74 impl IntoHList<#hlist_type> for #struct_name {
75 fn into_hlist(self) -> #hlist_type {
76 #hlist_init
77 }
78 }
79 };
80
81 TokenStream::from(expanded)
83}
84
85fn hlist_type(mut fields: syn::punctuated::Iter<Field>) -> proc_macro2::TokenStream {
88 match fields.next() {
89 Some(field) => {
90 let lhs = field.ty.to_token_stream();
91 let rhs = hlist_type(fields);
92 quote!(HCons<#lhs, #rhs>)
93 }
94 None => quote!(HNil),
95 }
96}
97
98fn hlist_pattern(mut fields: syn::punctuated::Iter<Field>) -> proc_macro2::TokenStream {
101 match fields.next() {
102 Some(field) => {
103 let lhs = field.ident.as_ref();
104 let rhs = hlist_pattern(fields);
105 quote!(HCons(#lhs, #rhs))
106 }
107 None => quote!(HNil),
108 }
109}
110
111fn hlist_init(mut fields: syn::punctuated::Iter<Field>) -> proc_macro2::TokenStream {
114 match fields.next() {
115 Some(field) => {
116 let lhs = field.ident.as_ref();
117 let rhs = hlist_init(fields);
118 quote!(HCons(self.#lhs, #rhs))
119 }
120 None => quote!(HNil),
121 }
122}
123
124fn hlist_cloned_init(mut fields: syn::punctuated::Iter<Field>) -> proc_macro2::TokenStream {
127 match fields.next() {
128 Some(field) => {
129 let lhs = field.ident.as_ref();
130 let rhs = hlist_cloned_init(fields);
131 quote!(HCons(self.#lhs.clone(), #rhs))
132 }
133 None => quote!(HNil),
134 }
135}
136
137fn struct_field_init(fields: syn::punctuated::Iter<Field>) -> proc_macro2::TokenStream {
139 let field_names = fields.map(|f| f.ident.as_ref());
140 quote!(#(#field_names),*)
141}