1use darling::{FromDeriveInput, FromField};
6use proc_macro2::{Span, TokenStream};
7use quote::{quote, TokenStreamExt};
8use syn::{self, parse_quote, DeriveInput, Field, Ident, WherePredicate};
9use synstructure::{self, BindStyle, BindingInfo, VariantInfo};
10
11pub(crate) fn parse_input_attrs<A>(input: &DeriveInput) -> A
12where
13 A: FromDeriveInput,
14{
15 match A::from_derive_input(input) {
16 Ok(attrs) => attrs,
17 Err(e) => panic!("failed to parse input attributes: {}", e),
18 }
19}
20
21pub(crate) fn parse_field_attrs<A>(field: &Field) -> A
22where
23 A: FromField,
24{
25 match A::from_field(field) {
26 Ok(attrs) => attrs,
27 Err(e) => panic!("failed to parse field attributes: {}", e),
28 }
29}
30
31pub(crate) fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) {
32 where_clause
33 .get_or_insert(parse_quote!(where))
34 .predicates
35 .push(pred);
36}
37
38pub(crate) fn fmap2_match<F, G>(
39 input: &DeriveInput,
40 bind_style: BindStyle,
41 mut f: F,
42 mut g: G,
43) -> TokenStream
44where
45 F: FnMut(&BindingInfo) -> TokenStream,
46 G: FnMut(&BindingInfo) -> Option<TokenStream>,
47{
48 let mut s = synstructure::Structure::new(input);
49 s.variants_mut().iter_mut().for_each(|v| {
50 v.bind_with(|_| bind_style);
51 });
52 s.each_variant(|variant| {
53 let (mapped, mapped_fields) = value(variant, "mapped");
54 let fields_pairs = variant.bindings().iter().zip(mapped_fields.iter());
55 let mut computations = quote!();
56 computations.append_all(fields_pairs.map(|(field, mapped_field)| {
57 let expr = f(field);
58 quote! { let #mapped_field = #expr; }
59 }));
60 computations.append_all(
61 mapped_fields
62 .iter()
63 .map(|mapped_field| match g(mapped_field) {
64 Some(expr) => quote! { let #mapped_field = #expr; },
65 None => quote!(),
66 }),
67 );
68 computations.append_all(mapped);
69 Some(computations)
70 })
71}
72
73fn value<'a>(variant: &'a VariantInfo, prefix: &str) -> (TokenStream, Vec<BindingInfo<'a>>) {
74 let mut v = variant.clone();
75 v.bindings_mut().iter_mut().for_each(|b| {
76 b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
77 });
78 v.bind_with(|_| BindStyle::Move);
79 (v.pat(), v.bindings().to_vec())
80}