arraygen 0.3.2

Derive macro for generating arrays from struct fields.
Documentation
use quote::quote;
use std::collections::HashMap;
use syn::parse::{Parse, ParseStream, Result};
use syn::token;
use syn::{bracketed, parenthesized, Error, Ident, Path, Token, Type, Visibility};

use crate::parse_attribute::single_parse_outer_attribute;
use crate::parse_decorator::{CastKind, Decorator};
use crate::parse_in_array::InArrayElement;
use crate::types::are_matching_types;
use crate::{DECL_FN_NAME, IMPLICIT_SELECT_ALL_NAME};

pub struct GenArray {
    pub vis: Visibility,
    pub fn_name: Ident,
    pub fn_ty: Type,
    pub is_mut: bool,
    pub is_ref: bool,
    pub implicit_select_all_tys: Vec<Type>,
    pub implicit_select_all_decorator: Decorator,
    pub casts: Vec<(syn::Ident, syn::Type, proc_macro2::TokenStream, CastKind)>,
    pub fields: Vec<InArrayElement>,
}

pub fn parse_gen_arrays(input: ParseStream) -> Result<HashMap<Ident, GenArray>> {
    let mut gen_arrays = HashMap::new();
    while input.peek(Token![#]) {
        if let Ok(true) = is_gen_array(&input.fork()) {
            let gen_array = input.call(single_parse_gen_array)?;
            gen_arrays.insert(gen_array.fn_name.clone(), gen_array);
        } else {
            input.call(single_parse_outer_attribute)?;
        }
    }
    Ok(gen_arrays)
}

pub fn single_parse_gen_array(input: ParseStream) -> Result<GenArray> {
    let content;
    let _: Token![#] = input.parse()?;
    let _: token::Bracket = bracketed!(content in input);
    let path: Path = content.call(Path::parse_mod_style)?;

    if path.segments.len() != 1 {
        return Err(input.error(format!("Wrong syntax for {}", DECL_FN_NAME)));
    }

    content.call(parse_gen_array_group)
}

pub fn is_gen_array(input: ParseStream) -> Result<bool> {
    let content;
    let _: Token![#] = input.parse()?;
    let _: token::Bracket = bracketed!(content in input);
    let path: Path = content.call(Path::parse_mod_style)?;

    Ok(!path.segments.is_empty() && path.segments[0].ident == DECL_FN_NAME)
}

pub fn parse_gen_array_group(input: ParseStream) -> Result<GenArray> {
    let content;
    let _ = parenthesized!(content in input);
    let vis: Visibility = content.parse()?;
    let _: Token![fn] = content.parse()?;
    let fn_name: Ident = content.parse()?;
    let _: Token![:] = content.parse()?;
    let fn_ty: Type = content.parse()?;

    let (is_ref, is_mut) = if let Type::Reference(ref reference) = fn_ty {
        (true, reference.mutability.is_some())
    } else {
        (false, false)
    };

    let mut implicit_select_all_tys = vec![];
    let mut implicit_select_all_decorator = Decorator::new();

    if content.peek(Token![,]) && content.peek2(syn::Ident) {
        let _: Token![,] = content.parse()?;
        let implicit: syn::Ident = content.parse()?;
        if implicit != IMPLICIT_SELECT_ALL_NAME {
            return Err(content.error(format!("clause '{}' not recognised", implicit)));
        }

        implicit_select_all_decorator = content.parse::<Decorator>()?;
        if implicit_select_all_decorator.override_implicit {
            return Err(Error::new_spanned(
                implicit,
                format!(
                    "{} method '{}' contains {} clause with forbidden decorator 'override_implicit'",
                    DECL_FN_NAME,
                    fn_name,
                    IMPLICIT_SELECT_ALL_NAME
                ),
            ));
        }

        let _: Token![:] = content.parse::<Token![:]>()?;
        implicit_select_all_tys = content
            .parse_terminated::<Type, Token![,]>(Type::parse)?
            .into_iter()
            .collect::<Vec<Type>>();

        if implicit_select_all_tys.is_empty() {
            return Err(content.error("missing type to select"));
        }

        for (i, ty_left) in implicit_select_all_tys.iter().enumerate() {
            for ty_right in implicit_select_all_tys.iter().skip(i + 1) {
                if are_matching_types(ty_left, ty_right) {
                    return Err(Error::new_spanned(
                        ty_right,
                        format!(
                            "{} method '{}' contains {} clause with duplicated '{}' type",
                            DECL_FN_NAME,
                            fn_name,
                            IMPLICIT_SELECT_ALL_NAME,
                            quote! { #ty_right }.to_string()
                        ),
                    ));
                }
            }
        }
    }

    Ok(GenArray {
        vis,
        fn_name,
        fn_ty,
        is_mut,
        is_ref,
        implicit_select_all_tys,
        implicit_select_all_decorator,
        casts: vec![],
        fields: vec![],
    })
}