/* Copyright ⓒ 2016 rust-custom-derive contributors. Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of ), at your option. All files in the project carrying such notice may not be copied, modified, or distributed except according to those terms. */ #![cfg_attr(feature="parse-generics-poc", feature(plugin))] #![cfg_attr(feature="parse-generics-poc", plugin(parse_generics_poc))] #[macro_use] extern crate parse_generics_shim; #[macro_use] extern crate custom_derive; #[macro_use] extern crate parse_macros; macro_rules! Reflect { (() $($tail:tt)*) => { parse_item! { then Reflect! { @item }, $($tail)* } }; (@as_item $i:item) => { $i }; ( @item struct { attrs: $_attrs:tt, vis: $vis:tt, name: $name:ident, generics: { constr: [ $($constr:tt)* ], params: [ $($params:tt)* ], ltimes: [ $($ltimes:tt,)* ], tnames: [ $($tnames:ident,)* ], }, where: { clause: $_clause:tt, preds: [ $($preds:tt)* ], }, kind: $_kind:tt, fields: $fields:tt, $($_tail:tt)* } ) => { Reflect! { @as_item impl<$($constr)*> Reflect for $name<$($params)*> where $($tnames: Reflect,)* $($preds)* { fn reflect() -> Type { let fields = Reflect!(@record_fields $fields); let item = Item { visibility: vis_to_visibility!($vis), module: module_path!(), name: stringify!($name), kind: ItemKind::Struct(Struct { fields: fields, }), }; Type::Item(item) } } } }; ( @record_fields [ $( { ord: $_ord:tt, attrs: $_attrs:tt, vis: $vis:tt, ty: $ty:ty, name: $name:ident, $(_field_tail:tt)* }, )* ] ) => { vec![ $( RecordField { visibility: vis_to_visibility!($vis), name: stringify!($name), ty: Box::new(<$ty as Reflect>::reflect()), }, )* ].into_boxed_slice() }; } macro_rules! vis_to_visibility { (()) => { Visibility::Private }; ((pub)) => { Visibility::Public }; } pub trait Reflect { fn reflect() -> Type; } impl Reflect for u8 { fn reflect() -> Type { Type::Lang(Lang::U8) } } impl Reflect for Option where T: Reflect { fn reflect() -> Type { Type::Item(Item { visibility: Visibility::Public, module: "core::option", name: "Option", kind: ItemKind::Enum(Enum { variants: vec![ EnumVariant { name: "None", kind: EnumVariantKind::Unitary, }, EnumVariant { name: "Some", kind: EnumVariantKind::Tuple(vec![ TupleField { ty: Box::new(::reflect()), }, ].into_boxed_slice()), }, ].into_boxed_slice(), }), }) } } #[derive(Debug)] pub enum Type { Lang(Lang), Item(Item), } #[derive(Debug)] pub enum Lang { U8, } #[derive(Debug)] pub struct Item { pub visibility: Visibility, pub module: &'static str, pub name: &'static str, pub kind: ItemKind, } #[derive(Debug)] pub enum ItemKind { Enum(Enum), Struct(Struct), } #[derive(Debug)] pub struct Enum { pub variants: Box<[EnumVariant]>, } #[derive(Debug)] pub struct EnumVariant { pub name: &'static str, pub kind: EnumVariantKind, } #[derive(Debug)] pub enum EnumVariantKind { Unitary, Tuple(Box<[TupleField]>), Record(Box<[RecordField]>), } #[derive(Debug)] pub struct Struct { pub fields: Box<[RecordField]>, } #[derive(Debug)] pub enum Visibility { Private, Public } #[derive(Debug)] pub struct TupleField { pub ty: Box, } #[derive(Debug)] pub struct RecordField { pub visibility: Visibility, pub name: &'static str, pub ty: Box, } custom_derive! { #[derive(Debug)] #[derive(Reflect)] struct Rgba where T: Copy { /// The red stuff r: T, #[doc="delicious green flavour"] g: T, pub b: T, /// Maybe alpha, maybe not. pub a: Option, } } macro_rules! aeqiws { ($lhs:expr, $rhs:expr) => { { let lhs = $lhs.replace(char::is_whitespace, ""); let rhs = $rhs.replace(char::is_whitespace, ""); if lhs != rhs { panic!("assertion failed: `(left == right)` (left: `{:?}`, right: `{:?}`)", lhs, rhs); } } }; } #[test] fn test_reflect() { aeqiws!( format!("{:?}", Rgba::::reflect()), r#" Item(Item { visibility: Private, module: "reflect", name: "Rgba", kind: Struct(Struct { fields: [ RecordField { visibility: Private, name: "r", ty: Lang(U8) }, RecordField { visibility: Private, name: "g", ty: Lang(U8) }, RecordField { visibility: Public, name: "b", ty: Lang(U8) }, RecordField { visibility: Public, name: "a", ty: Item(Item { visibility: Public, module: "core::option", name: "Option", kind: Enum(Enum { variants: [ EnumVariant { name: "None", kind: Unitary }, EnumVariant { name: "Some", kind: Tuple([ TupleField { ty: Lang(U8) } ]) } ] }) }) } ] }) }) "# ); }