kv_derive_macro/
lib.rs

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/// Derives [`kv_derive::into_vec::IntoVec`].
11#[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/// Derives [`kv_derive::from_iter::FromIter`].
63#[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/// Derives [`kv_derive::from_mapping::FromMapping`].
120#[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}