use syntax::ast; use syntax::codemap; use syntax::ext::base::ExtCtxt; use syntax::ptr::P; use attr; use Error; pub struct Item<'a> { pub ident: ast::Ident, pub span: codemap::Span, pub attrs: attr::Item, pub body: Body<'a>, pub generics: &'a ast::Generics, } pub enum Body<'a> { Enum(Vec>), Struct(Style, Vec>), } pub struct Variant<'a> { pub ident: ast::Ident, pub span: codemap::Span, pub attrs: attr::Variant, pub style: Style, pub fields: Vec>, } pub struct Field<'a> { pub ident: Option, pub span: codemap::Span, pub attrs: attr::Field, pub ty: &'a P, } pub enum Style { Struct, Tuple, Newtype, Unit, } impl<'a> Item<'a> { pub fn from_ast( cx: &ExtCtxt, item: &'a ast::Item, ) -> Result, Error> { let attrs = attr::Item::from_ast(cx, item); let (body, generics) = match item.node { ast::ItemKind::Enum(ref enum_def, ref generics) => { let variants = enum_from_ast(cx, enum_def); (Body::Enum(variants), generics) } ast::ItemKind::Struct(ref variant_data, ref generics) => { let (style, fields) = struct_from_ast(cx, variant_data); (Body::Struct(style, fields), generics) } _ => { return Err(Error::UnexpectedItemKind); } }; Ok(Item { ident: item.ident, span: item.span, attrs: attrs, body: body, generics: generics, }) } } impl<'a> Body<'a> { pub fn all_fields(&'a self) -> Box> + 'a> { match *self { Body::Enum(ref variants) => { Box::new(variants.iter() .flat_map(|variant| variant.fields.iter())) } Body::Struct(_, ref fields) => { Box::new(fields.iter()) } } } } fn enum_from_ast<'a>( cx: &ExtCtxt, enum_def: &'a ast::EnumDef, ) -> Vec> { enum_def.variants.iter() .map(|variant| { let (style, fields) = struct_from_ast(cx, &variant.node.data); Variant { ident: variant.node.name, span: variant.span, attrs: attr::Variant::from_ast(cx, variant), style: style, fields: fields, } }) .collect() } fn struct_from_ast<'a>( cx: &ExtCtxt, variant_data: &'a ast::VariantData, ) -> (Style, Vec>) { match *variant_data { ast::VariantData::Struct(ref fields, _) => { (Style::Struct, fields_from_ast(cx, fields)) } ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { (Style::Newtype, fields_from_ast(cx, fields)) } ast::VariantData::Tuple(ref fields, _) => { (Style::Tuple, fields_from_ast(cx, fields)) } ast::VariantData::Unit(_) => { (Style::Unit, Vec::new()) } } } fn fields_from_ast<'a>( cx: &ExtCtxt, fields: &'a [ast::StructField], ) -> Vec> { fields.iter() .enumerate() .map(|(i, field)| { Field { ident: field.ident, span: field.span, attrs: attr::Field::from_ast(cx, i, field), ty: &field.ty, } }) .collect() }