nesting 0.1.1

Nesting structs, enums, and impls, in Rust!
Documentation
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::{
    AttrStyle, Attribute, Ident, ItemFn, ItemImpl, Result, Token, Type, Visibility,
    parse::{Parse, ParseStream},
    spanned::Spanned,
};

mod enums;
mod structs;

use enums::VariantDef;
use structs::StructKind;

#[derive(PartialEq, Debug)]
pub enum AttrScope {
    Struct,
    Enum,
    Impl,
    StructAndEnum,
    All,
}

pub struct NestInput {
    pub global_attrs: Vec<(AttrScope, Attribute)>,
    pub items: Vec<ItemDef>,
}

impl Parse for NestInput {
    fn parse(input: ParseStream) -> Result<Self> {
        let global_attrs: Vec<(AttrScope, Attribute)> = input
            .call(Attribute::parse_inner)?
            .into_iter()
            .map(|mut attr| {
                attr.style = AttrStyle::Outer;

                let attr_str = attr.path().get_ident();

                if let Some(attr_name) = attr_str {
                    let attr_string = attr_name.to_string();
                    let scope = if attr_string.as_str() == "structs" {
                        AttrScope::Struct
                    } else if attr_string.as_str() == "enums" {
                        AttrScope::Enum
                    } else if attr_string.as_str() == "impls" {
                        AttrScope::Impl
                    } else if attr_string.as_str() == "all" {
                        AttrScope::All
                    } else {
                        return (AttrScope::StructAndEnum, attr);
                    };

                    if let syn::Meta::List(meta_list) = &attr.meta {
                        if let Ok(nested) = meta_list.parse_args::<syn::Meta>() {
                            attr.meta = nested;
                        }
                    }

                    return (scope, attr);
                };

                (AttrScope::StructAndEnum, attr)
            })
            .collect();

        let mut items = Vec::new();

        while !input.is_empty() {
            items.push(input.parse::<ItemDef>()?);
        }

        Ok(NestInput {
            global_attrs,
            items,
        })
    }
}

pub enum ItemDef {
    /// A struct definition
    Struct(structs::StructDef),
    /// An enum definition
    Enum(enums::EnumDef),
    /// An impl block
    Impl(ItemImpl),
    /// Loose function
    LooseFunc { func: ItemFn, parent: Option<Ident> },
}

impl ItemDef {
    pub fn flatten(self) -> Vec<ItemDef> {
        let mut result = Vec::new();

        let mut flattened_children = Vec::new();
        match self {
            ItemDef::Struct(struct_def) => {
                for nested in struct_def.nested_items {
                    flattened_children.extend(nested.flatten())
                }
                result.push(ItemDef::Struct(structs::StructDef {
                    nested_items: Vec::new(),
                    ..struct_def
                }));
            }
            ItemDef::Enum(enum_def) => {
                for nested in enum_def.nested_items {
                    flattened_children.extend(nested.flatten())
                }
                result.push(ItemDef::Enum(enums::EnumDef {
                    nested_items: Vec::new(),
                    ..enum_def
                }));
            }
            ItemDef::Impl(i) => {
                result.push(ItemDef::Impl(i));
            }
            ItemDef::LooseFunc { func: item, parent } => {
                result.push(ItemDef::LooseFunc { func: item, parent });
            }
        }

        result.extend(flattened_children);

        result
    }

    pub fn render(&self, global_attrs: &Vec<(AttrScope, Attribute)>) -> TokenStream {
        #[inline]
        fn attrs_from_type<'a>(
            scope: AttrScope,
            attrs: &'a Vec<(AttrScope, Attribute)>,
        ) -> Vec<&'a Attribute> {
            let is_struct_and_enum = matches!(scope, AttrScope::Struct | AttrScope::Enum);

            attrs
                .into_iter()
                .filter(|&(attr_scope, _)| {
                    (is_struct_and_enum && attr_scope == &AttrScope::StructAndEnum)
                        || attr_scope == &AttrScope::All
                        || attr_scope == &scope
                })
                .map(|(_, attr)| attr)
                .collect()
        }

        match self {
            ItemDef::Struct(s) => match s.kind {
                StructKind::FieldStruct => {
                    let fields = s.fields.iter().map(|f| {
                        let attrs = &f.attrs;
                        let vis = &f.vis;
                        let name = &f.name;
                        let ty = &f.ty;

                        quote! {
                            #(#attrs)*
                            #vis #name: #ty,
                        }
                    });

                    let attrs = &s.attrs;
                    let vis = &s.vis;
                    let name = &s.name;

                    let global_attrs = attrs_from_type(AttrScope::Struct, global_attrs);

                    quote_spanned! {name.span()=>
                        #(#global_attrs)*
                        #(#attrs)*
                        #vis struct #name {
                            #(#fields)*
                        }
                    }
                }
                StructKind::TupleStruct => {
                    let fields = s.fields.iter().map(|f| {
                        let attrs = &f.attrs;
                        let vis = &f.vis;
                        let ty = &f.ty;

                        quote! {
                            #(#attrs)*
                            #vis #ty,
                        }
                    });

                    let attrs = &s.attrs;
                    let vis = &s.vis;
                    let name = &s.name;

                    let global_attrs = attrs_from_type(AttrScope::Struct, global_attrs);

                    quote_spanned! {name.span()=>
                        #(#global_attrs)*
                        #(#attrs)*
                        #vis struct #name(#(#fields)*);
                    }
                }
                StructKind::MarkerStruct => {
                    let attrs = &s.attrs;
                    let vis = &s.vis;
                    let name = &s.name;

                    let global_attrs = attrs_from_type(AttrScope::Struct, global_attrs);

                    quote_spanned! {name.span()=>
                        #(#global_attrs)*
                        #(#attrs)*
                        #vis struct #name;
                    }
                }
            },
            ItemDef::Enum(e) => {
                let fields = e.variants.iter().map(|v| match v {
                    VariantDef::Struct(s) => {
                        let attrs = &s.attrs;
                        let name = &s.name;

                        let fields = s.fields.iter().map(|f| {
                            let attrs = &f.attrs;
                            let vis = &f.vis;
                            let name = &f.name;
                            let ty = &f.ty;

                            quote! {
                                #(#attrs)*
                                #vis #name: #ty,
                            }
                        });

                        quote_spanned! {name.span()=>
                            #(#attrs)*
                            #name {
                                #(#fields)*
                            },
                        }
                    }
                    VariantDef::Tuple(t) => {
                        let attrs = &t.attrs;
                        let name = &t.name;

                        let fields = t.fields.iter().map(|f| {
                            let attrs = &f.attrs;
                            let vis = &f.vis;
                            let ty = &f.ty;

                            quote! {
                                #(#attrs)*
                                #vis #ty,
                            }
                        });

                        quote_spanned! {name.span()=>
                            #(#attrs)*
                            #name(#(#fields)*),
                        }
                    }
                    VariantDef::Unit(u) => {
                        let attrs = &u.attrs;
                        let name = &u.name;

                        quote_spanned! {name.span()=>
                            #(#attrs)*
                            #name,
                        }
                    }
                });

                let attrs = &e.attrs;
                let vis = &e.vis;
                let name = &e.name;

                let global_attrs = attrs_from_type(AttrScope::Enum, global_attrs);

                quote_spanned! {name.span()=>
                    #(#global_attrs)*
                    #(#attrs)*
                    #vis enum #name {
                        #(#fields)*
                    }
                }
            }
            ItemDef::Impl(impl_block) => {
                let ty = &impl_block.self_ty;

                let global_attrs = attrs_from_type(AttrScope::Impl, global_attrs);

                quote_spanned! {ty.span()=>
                    #(#global_attrs)*
                    #impl_block
                }
            }
            ItemDef::LooseFunc { func, parent } => {
                let vis = &func.vis;
                let sig = &func.sig;
                let block = &func.block;

                if let Some(parent) = parent {
                    let global_attrs = attrs_from_type(AttrScope::Impl, global_attrs);

                    quote_spanned! {sig.span()=>
                        #(#global_attrs)*
                        impl #parent {
                            #vis #sig #block
                        }
                    }
                } else {
                    quote! {
                        compile_error!("Missing LooseFunc parent type. This should never happen.");
                    }
                }
            }
        }
    }
}

impl Parse for ItemDef {
    fn parse(input: ParseStream) -> Result<Self> {
        let lookahead = input.fork();
        lookahead.call(Attribute::parse_outer)?;
        lookahead.call(Visibility::parse)?;

        if lookahead.peek(Token![struct]) {
            let struct_def = input.parse::<structs::StructDef>()?;
            Ok(ItemDef::Struct(struct_def))
        } else if lookahead.peek(Token![enum]) {
            let enum_def = input.parse::<enums::EnumDef>()?;
            Ok(ItemDef::Enum(enum_def))
        } else if lookahead.peek(Token![impl]) {
            let impl_block = input.parse::<ItemImpl>()?;
            Ok(ItemDef::Impl(impl_block))
        } else if lookahead.peek(Token![fn]) {
            let func = input.parse::<ItemFn>()?;
            Ok(ItemDef::LooseFunc { func, parent: None })
        } else {
            Err(input.error("expected struct, enum, impl, or fn"))
        }
    }
}

pub struct FieldDef {
    attrs: Vec<Attribute>,
    vis: Visibility,
    name: Ident,
    ty: Type,
}