arraygen 0.3.2

Derive macro for generating arrays from struct fields.
Documentation
use crate::DECL_FN_NAME;
use std::collections::HashMap;
use syn::parse::{Parse, ParseStream, Result};
use syn::token;
use syn::{braced, Error, Generics, Ident, Token, Visibility, WhereClause};

use crate::parse_attribute::parse_inner_attributes;
use crate::parse_gen_array::{parse_gen_arrays, GenArray};
use crate::parse_in_array::{
    parse_in_array_fields, InArrayElement, InArrayElementKind, InArrayField,
};
use crate::types::ty_inferred_by;

pub struct DeriveArraygen {
    pub gen_arrays: HashMap<Ident, GenArray>,
    pub vis: Visibility,
    pub struct_name: Ident,
    pub generics: Generics,
}

impl Parse for DeriveArraygen {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut gen_arrays = input.call(parse_gen_arrays)?;
        let vis = input.parse::<Visibility>()?;

        let lookahead = input.lookahead1();
        if !lookahead.peek(Token![struct]) {
            return Err(input.error("derive 'Arraygen' should only be used with braced structs"));
        }

        let _ = input.parse::<Token![struct]>()?;
        let struct_name = input.parse::<Ident>()?;
        let generics = input.parse::<Generics>()?;
        let where_clause = parse_struct(input, &mut gen_arrays)?;

        Ok(DeriveArraygen {
            gen_arrays,
            vis,
            struct_name,
            generics: Generics {
                where_clause,
                ..generics
            },
        })
    }
}

pub fn parse_struct(
    input: ParseStream,
    gen_arrays: &mut HashMap<Ident, GenArray>,
) -> Result<Option<WhereClause>> {
    let mut lookahead = input.lookahead1();
    let mut where_clause = None;
    if lookahead.peek(Token![where]) {
        where_clause = Some(input.parse()?);
        lookahead = input.lookahead1();
    }

    if lookahead.peek(token::Brace) {
        parse_braced_struct(input, gen_arrays)?;
        Ok(where_clause)
    } else {
        Err(input.error("derive 'Arraygen' should only be used with braced structs"))
    }
}

pub(crate) fn parse_braced_struct(
    input: ParseStream,
    gen_arrays: &mut HashMap<Ident, GenArray>,
) -> Result<()> {
    let content;
    let _ = braced!(content in input);
    parse_inner_attributes(&content)?;
    for iaf in content
        .parse_terminated::<InArrayField, Token![,]>(parse_in_array_fields)?
        .into_iter()
    {
        for (_, ga) in gen_arrays.iter_mut() {
            for implicit_ty in ga.implicit_select_all_tys.iter() {
                if ty_inferred_by(&iaf.ty, implicit_ty) {
                    ga.fields.push(InArrayElement {
                        ident: iaf.ident.clone(),
                        ty: iaf.ty.clone(),
                        cast: ga.implicit_select_all_decorator.cast.clone(),
                        kind: InArrayElementKind::Implicit,
                    });
                }
            }
        }
        for attr in iaf.attrs.iter() {
            for entry in attr.entries.iter() {
                if let Some(ga) = gen_arrays.get_mut(&entry.ident) {
                    let iae = ga.fields.iter().find(|iae| iae.ident == iaf.ident);
                    if matches!(iae, Some(iae) if iae.kind != InArrayElementKind::Implicit || (iae.kind == InArrayElementKind::Implicit && !entry.decorator.override_implicit))
                    {
                        return Err(Error::new_spanned(
                            entry.ident.clone(),
                            format!(
                                "Field '{}' is already included in {} method '{}'",
                                iaf.ident.to_string(),
                                DECL_FN_NAME,
                                entry.ident.to_string()
                            ),
                        ));
                    } else {
                        if let Some(ident) = iae.map(|iae| iae.ident.clone()) {
                            ga.fields.retain(|field| field.ident != ident);
                        }

                        ga.fields.push(InArrayElement {
                            ident: iaf.ident.clone(),
                            ty: iaf.ty.clone(),
                            cast: entry.decorator.cast.clone(),
                            kind: InArrayElementKind::InArray,
                        });
                    }
                } else {
                    return Err(Error::new_spanned(
                        entry.ident.clone(),
                        format!(
                            "{} method '{}' not present but used by field '{}'",
                            DECL_FN_NAME,
                            entry.ident.to_string(),
                            iaf.ident.to_string()
                        ),
                    ));
                }
            }
        }
    }
    Ok(())
}