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 {
Struct(structs::StructDef),
Enum(enums::EnumDef),
Impl(ItemImpl),
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,
}