structural-typing-macros 0.1.6

Procedural macros for structural-typing crate
Documentation
use proc_macro2::TokenStream;
use quote::quote;
use syn::{GenericParam, Generics, Ident};

/// Extracts just the identifiers from generic parameters for use in type arguments
pub fn extract_type_param_idents(params: &syn::punctuated::Punctuated<GenericParam, syn::token::Comma>) -> Vec<TokenStream> {
    params
        .iter()
        .map(|param| match param {
            GenericParam::Type(type_param) => {
                let ident = &type_param.ident;
                quote! { #ident }
            }
            GenericParam::Lifetime(lifetime_param) => {
                let lifetime = &lifetime_param.lifetime;
                quote! { #lifetime }
            }
            GenericParam::Const(const_param) => {
                let ident = &const_param.ident;
                quote! { #ident }
            }
        })
        .collect()
}

/// Creates impl generics by adding F parameter after lifetimes but before other params
/// Also returns user type args for convenience
pub fn impl_generics_with_f(user_generics: &Generics, module_name: &Ident) -> (Generics, Vec<TokenStream>) {
    let mut impl_generics = user_generics.clone();
    let f_param: GenericParam = syn::parse_quote!(F: #module_name::Fields);

    // Find insertion point: after all lifetimes
    let mut insertion_point = impl_generics.params.len();
    for (i, param) in impl_generics.params.iter().enumerate() {
        if !matches!(param, GenericParam::Lifetime(_)) {
            insertion_point = i;
            break;
        }
    }

    impl_generics.params.insert(insertion_point, f_param);

    let user_type_args = extract_type_param_idents(&user_generics.params);

    (impl_generics, user_type_args)
}

/// Generates remainder type arguments with proper lifetime ordering
/// Returns <lifetimes, F, F2, other_params> or <F, F2> if no user generics
pub fn remainder_type_args(generics: &Generics, user_type_args: &[TokenStream]) -> TokenStream {
    if generics.params.is_empty() {
        quote! { <F, F2> }
    } else {
        let (lifetime_args, other_args): (Vec<_>, Vec<_>) = user_type_args
            .iter()
            .zip(generics.params.iter())
            .partition(|(_, param)| matches!(param, GenericParam::Lifetime(_)));
        let lifetime_tokens: Vec<_> = lifetime_args.into_iter().map(|(tok, _)| tok).collect();
        let other_tokens: Vec<_> = other_args.into_iter().map(|(tok, _)| tok).collect();
        quote! { <#(#lifetime_tokens,)* F, F2, #(#other_tokens),*> }
    }
}