1use proc_macro::TokenStream;
2use quote::quote;
3
4use crate::field::Field;
5use crate::opts::{get_fields, parse_opts, MacroOpts};
6
7mod field;
8mod opts;
9
10#[proc_macro_derive(IntoVec, attributes(kv))]
12pub fn derive_into_vec(input: TokenStream) -> TokenStream {
13 let opts: MacroOpts = parse_opts(input);
14 let ident = opts.ident;
15 let generics = opts.generics;
16 let field_producers = get_fields(opts.data)
17 .into_iter()
18 .map(generate_field_producer);
19
20 let tokens = quote! {
21 impl #generics ::kv_derive::into_vec::IntoVec for #ident #generics {
22 fn into_iter(self) -> Box<dyn Iterator<Item = (String, String)>> {
23 Box::new(
24 (std::iter::empty())
25 #(#field_producers)*
26 )
27 }
28 }
29 };
30 tokens.into()
31}
32
33fn generate_field_producer(field: Field) -> proc_macro2::TokenStream {
34 let ident = field.get_ident();
35 let key = field.get_key();
36 let value = quote! { self.#ident };
37
38 let mut producer = if let Some(flatten) = &field.flatten {
39 if let Some(prefix) = &flatten.prefix {
40 quote! { (::kv_derive::producer::PrefixedFlatteningProducer(#prefix)) }
41 } else {
42 quote! { (::kv_derive::producer::FlatteningProducer) }
43 }
44 } else {
45 quote! { ::kv_derive::producer::ScalarProducer }
46 };
47 if let Some(into_repr_with) = field.into_repr_with {
48 producer = quote! { ::kv_derive::producer::WrappedProducer(#producer, #into_repr_with) };
49 }
50 if field.is_optional {
51 producer = quote! { ::kv_derive::producer::OptionProducer(#producer) };
52 }
53 if field.is_collection {
54 producer = quote! { ::kv_derive::producer::CollectionProducer(#producer) };
55 }
56
57 quote! {
58 .chain((#producer).produce(#key, (#value)))
59 }
60}
61
62#[proc_macro_derive(FromIter, attributes(kv))]
64pub fn derive_from_iter(input: TokenStream) -> TokenStream {
65 let opts: MacroOpts = parse_opts(input);
66 let ident = opts.ident;
67 let generics = opts.generics;
68 let fields = get_fields(opts.data);
69
70 let field_consumers = fields.iter().map(generate_match_field_consumer);
71
72 let field_defaults = fields.iter().map(|field| {
73 let ident = field.get_ident();
74 let ty = &field.ty;
75 let default_opts = field
76 .default
77 .as_ref()
78 .expect("`FromIter` requires `#[kv(default(…))]` on each field");
79 if let Some(value) = &default_opts.value {
80 quote! { #ident: (#value), }
81 } else {
82 quote! { #ident: (<#ty as std::default::Default>::default()), }
83 }
84 });
85
86 let tokens = quote! {
87 impl #generics ::kv_derive::from_iter::FromIter for #ident #generics {
88 fn from_iter<'a>(iter: impl std::iter::IntoIterator<Item = (&'a str, &'a str)>) -> ::kv_derive::result::Result<Self> {
89 let mut this = Self {
90 #(#field_defaults)*
91 };
92 for (key, value) in iter.into_iter() {
93 match key {
94 #(#field_consumers)*
95 _ => {}
96 }
97 }
98 Ok(this)
99 }
100 }
101 };
102 tokens.into()
103}
104
105fn generate_match_field_consumer(field: &Field) -> proc_macro2::TokenStream {
106 assert!(
107 field.flatten.is_none(),
108 "restoring a flattened field from an iterable is not implemented",
109 );
110
111 let ident = field.get_ident();
112 let key = field.get_key();
113 let consumer = field.consumer();
114 let value = field.wrap_from_repr_with(quote! { (std::str::FromStr::from_str(value))? });
115
116 quote! { #key => { #consumer.consume(&mut this.#ident, (#value)); } }
117}
118
119#[proc_macro_derive(FromMapping, attributes(kv))]
121pub fn derive_from_mapping(input: TokenStream) -> TokenStream {
122 let opts: MacroOpts = parse_opts(input);
123 let ident = opts.ident;
124 let generics = opts.generics;
125 let mapped_fields = get_fields(opts.data).into_iter().map(generate_mapped_field);
126
127 let tokens = quote! {
128 impl #generics ::kv_derive::from_mapping::FromMapping for #ident #generics {
129 fn from_mapping(mapping: impl Mapping) -> ::kv_derive::result::Result<Self> {
130 Ok(Self {
131 #(#mapped_fields)*
132 })
133 }
134 }
135
136 impl std::convert::TryFrom<std::collections::HashMap<String, String>> for #ident #generics {
137 type Error = ::kv_derive::error::Error;
138
139 #[inline]
140 fn try_from(mapping: std::collections::HashMap<String, String>) -> ::kv_derive::result::Result<Self> {
141 Self::from_mapping(mapping)
142 }
143 }
144 };
145 tokens.into()
146}
147
148fn generate_mapped_field(field: Field) -> proc_macro2::TokenStream {
149 let ident = field.get_ident();
150 assert!(
151 field.flatten.is_none() || field.default.is_none(),
152 "cannot use `flatten` and `default` at the same time for `{}`",
153 ident,
154 );
155
156 if let Some(flatten) = &field.flatten {
157 let mut mapping = quote! { mapping };
158 if let Some(prefix) = &flatten.prefix {
159 mapping = quote! { (::kv_derive::from_mapping::PrefixedMapping((#mapping), #prefix)) }
160 };
161 quote! { #ident: ::kv_derive::from_mapping::FromMapping::from_mapping((#mapping))?, }
162 } else {
163 let key = field.get_key();
164
165 let missing_handler = if let Some(default) = &field.default {
166 if let Some(value) = &default.value {
167 quote! { Ok(#value) }
168 } else {
169 quote! { Ok(std::default::Default::default()) }
170 }
171 } else {
172 quote! { Err(::kv_derive::error::Error::MissingKey(#key)) }
173 };
174
175 let consumer = field.consumer();
176 let value = field.wrap_from_repr_with(quote! { std::str::FromStr::from_str(value)? });
177
178 quote! {
179 #ident: mapping
180 .get_value(#key)
181 .map_or_else(
182 || #missing_handler,
183 |value| ::kv_derive::result::Result::Ok(#consumer.init(#value)),
184 )?,
185 }
186 }
187}