fn derive_iterate()

in amzn-smt-ir-derive/src/lib.rs [203:270]


fn derive_iterate(s: &synstructure::Structure) -> TokenStream {
    let smt_ir = smt_ir_crate_path();

    #[allow(non_snake_case)]
    let (Term, Logic, Iterate, Args) = (
        quote!(#smt_ir::Term),
        quote!(#smt_ir::Logic),
        quote!(#smt_ir::term::args::Iterate),
        quote!(#smt_ir::term::args::Arguments),
    );

    fn argument_iter_branches(
        s: &synstructure::Structure,
        mut iterate: impl FnMut(&synstructure::BindingInfo) -> TokenStream,
    ) -> TokenStream {
        s.each_variant(|v| {
            let mut bindings = (v.bindings().iter())
                .skip_while(|field| index_array(&field.ast().ty).is_some())
                .map(&mut iterate);
            let mut iter = bindings
                .next()
                .unwrap_or_else(|| quote!(std::iter::empty()));
            for new in bindings {
                iter = quote!(#iter.chain(#new))
            }
            // TODO: instead of boxing, could also make an enum -- might be worth it
            quote!(std::boxed::Box::new(#iter))
        })
    }

    let mut where_clause = syn::WhereClause {
        where_token: Default::default(),
        predicates: Default::default(),
    };

    bound_argument_fields(
        s,
        &mut where_clause,
        |ty| parse_quote!(#ty: #Iterate<'a, L>),
    );

    let args_branches = argument_iter_branches(s, |field| quote!(#Iterate::<L>::terms(#field)));
    let into_args_branches =
        argument_iter_branches(s, |field| quote!(#Iterate::<L>::into_terms(#field)));

    s.gen_impl(quote! {
        gen impl<'a, L: #Logic> #Iterate<'a, L> for @Self
        #where_clause
        {
            type Terms = std::boxed::Box<dyn std::iter::Iterator<Item = &'a #Term<L>> + 'a>;
            type IntoTerms = std::boxed::Box<dyn std::iter::Iterator<Item = #Term<L>> + 'a>;

            fn terms(&'a self) -> Self::Terms {
                match self {
                    #args_branches
                }
            }

            fn into_terms(self) -> Self::IntoTerms {
                match self {
                    #into_args_branches
                }
            }
        }

        gen impl<'a, L: #Logic> #Args<'a, L> for @Self #where_clause {}
    })
}