├── .gitignore ├── README.md ├── reflect ├── Cargo.lock ├── Cargo.toml ├── src │ ├── attributes.rs │ ├── lib.rs │ ├── reflect.rs │ ├── type_info.rs │ └── types.rs └── tests │ ├── test_reflect.rs │ └── tests.rs └── reflect_mac ├── Cargo.toml ├── src └── lib.rs └── tests └── tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | */target 2 | */Cargo.lock 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reflection for Rust 2 | 3 | This is a small reflection library for the Rust programming language. 4 | 5 | It is still in very early development, and is far from feature complete. The library incurs zero 6 | run-time penalty for features you don't use, but may slightly increase your binary sizes. 7 | 8 | The library is meant to aid in the implementation of things like web frameworks and ORMs. 9 | 10 | WARNING: rust-reflect currently uses experimental and unstable compiler features, and things may break 11 | unexpectedly as new versions of the Rust compiler are released. 12 | 13 | ## Example 14 | 15 | ```rust 16 | #![feature(phase)] 17 | 18 | extern crate reflect; 19 | #[phase(plugin)] 20 | extern crate reflect_mac; 21 | 22 | use reflect::{Reflect, ReflectRefExt, GetType, Type}; 23 | 24 | #[reflect] 25 | struct Foo { 26 | foo: i32 27 | } 28 | 29 | fn main() { 30 | let foo = Foo { foo: 123 }; 31 | 32 | println!("Name of type: {}", GetType::of::().name()); 33 | 34 | match foo.get("foo") { 35 | Ok(x) => match (*x).downcast_ref::() { 36 | Some(n) => println!("foo.foo = {}", n), 37 | None => println!("foo.foo was not an i32!") 38 | }, 39 | Err(_) => println!("foo.foo does not exist") 40 | } 41 | } 42 | ``` 43 | 44 | # TODOs 45 | 46 | - Composites / runtime structs 47 | - Enums 48 | - Observers 49 | - Signals/slots 50 | - Getter/setter attributes 51 | - `#[omit_reflect]` attribute 52 | - Serialization/deserialization interface 53 | -------------------------------------------------------------------------------- /reflect/Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "reflect" 3 | version = "0.0.1" 4 | dependencies = [ 5 | "phf 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "phf_mac 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 7 | "reflect_mac 0.0.1", 8 | ] 9 | 10 | [[package]] 11 | name = "gcc" 12 | version = "0.1.2" 13 | source = "registry+https://github.com/rust-lang/crates.io-index" 14 | 15 | [[package]] 16 | name = "log" 17 | version = "0.1.4" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | dependencies = [ 20 | "regex 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 21 | ] 22 | 23 | [[package]] 24 | name = "phf" 25 | version = "0.4.2" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | dependencies = [ 28 | "phf_shared 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 29 | ] 30 | 31 | [[package]] 32 | name = "phf_mac" 33 | version = "0.4.2" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | dependencies = [ 36 | "phf_shared 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 37 | "time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 38 | ] 39 | 40 | [[package]] 41 | name = "phf_shared" 42 | version = "0.4.2" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | 45 | [[package]] 46 | name = "reflect_mac" 47 | version = "0.0.1" 48 | dependencies = [ 49 | "phf 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 50 | "phf_mac 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 51 | ] 52 | 53 | [[package]] 54 | name = "regex" 55 | version = "0.1.4" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | 58 | [[package]] 59 | name = "rustc-serialize" 60 | version = "0.1.5" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | 63 | [[package]] 64 | name = "time" 65 | version = "0.1.4" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | dependencies = [ 68 | "gcc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 69 | "log 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 70 | "rustc-serialize 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 71 | ] 72 | 73 | -------------------------------------------------------------------------------- /reflect/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reflect" 3 | version = "0.0.1" 4 | authors = ["Simon Ask Ulsnes "] 5 | license = "MIT" 6 | 7 | [dependencies] 8 | phf = "*" 9 | phf_mac = "*" 10 | 11 | [dependencies.reflect_mac] 12 | path = "../reflect_mac" 13 | version = "=0.0.1" 14 | 15 | [[test]] 16 | name = "tests" 17 | -------------------------------------------------------------------------------- /reflect/src/attributes.rs: -------------------------------------------------------------------------------- 1 | use reflect::{Reflect, ReflectRefExt, ReflectMutRefExt}; 2 | use type_info::{Type, GetType, GetTypeInfo}; 3 | use phf; 4 | 5 | pub enum AttrError { 6 | WrongTargetType, 7 | WrongValueType, 8 | UnknownAttribute, 9 | } 10 | impl Copy for AttrError {} 11 | 12 | pub type AttrResult = Result; 13 | 14 | /// Attribute where both the owner type and the field type are known. 15 | pub trait Attribute { 16 | fn get(&self, owner: &O) -> AttrResult; 17 | fn set(&self, owner: &mut O, new_value: T) -> AttrResult<()>; 18 | } 19 | 20 | /// Attribute where only the field type is known. 21 | pub trait FieldAttribute { 22 | fn get(&self, owner: &Reflect) -> AttrResult; 23 | fn set(&self, owner: &mut Reflect, new_value: T) -> AttrResult<()>; 24 | } 25 | 26 | /// Attribute where only the owner type is known. 27 | pub trait OwnerAttribute: Sync + 'static { 28 | fn get(&self, owner: &O) -> AttrResult>; 29 | fn set(&self, owner: &mut O, new_value: &Reflect) -> AttrResult<()>; 30 | fn type_info(&self) -> &'static Type<'static>; 31 | } 32 | 33 | /// Attribute where neither the owner type nor the field type are known. 34 | pub trait AnyAttribute: Sync + 'static { 35 | fn get(&self, owner: &Reflect) -> AttrResult>; 36 | fn set(&self, owner: &mut Reflect, new_value: &Reflect) -> AttrResult<()>; 37 | fn type_info(&self) -> &'static Type<'static>; 38 | } 39 | 40 | /// A map of named attributes. 41 | pub type AttributeMap = phf::Map<&'static str, fn() -> &'static AnyAttribute>; 42 | 43 | impl FieldAttribute for X 44 | where X: Attribute, O: Reflect + 'static 45 | { 46 | fn get(&self, owner: &Reflect) -> AttrResult { 47 | match owner.downcast_ref::() { 48 | Some(o) => self.get(o), 49 | None => Err(AttrError::WrongTargetType) 50 | } 51 | } 52 | 53 | fn set(&self, owner: &mut Reflect, new_value: T) -> AttrResult<()> { 54 | match owner.downcast_mut::() { 55 | Some(o) => self.set(o, new_value), 56 | None => Err(AttrError::WrongTargetType) 57 | } 58 | } 59 | } 60 | 61 | impl OwnerAttribute for X 62 | where X: Attribute + Sync + 'static, T: GetTypeInfo + Reflect + Clone + 'static, O: 'static 63 | { 64 | fn get(&self, owner: &O) -> AttrResult> { 65 | let v = box try!((self as &Attribute).get(owner)); 66 | Ok(v as Box) 67 | } 68 | 69 | fn set(&self, owner: &mut O, new_value: &Reflect) -> AttrResult<()> { 70 | match new_value.downcast_ref::() { 71 | Some(x) => { 72 | (self as &Attribute).set(owner, (*x).clone()) 73 | }, 74 | None => Err(AttrError::WrongValueType) 75 | } 76 | } 77 | 78 | fn type_info(&self) -> &'static Type<'static> { 79 | GetType::of::() as &Type<'static> 80 | } 81 | } 82 | 83 | impl AnyAttribute for X 84 | where X: Attribute + Sync + 'static, T: GetTypeInfo + Reflect + Clone + 'static, O: Reflect + 'static 85 | { 86 | fn get(&self, owner: &Reflect) -> AttrResult> { 87 | match owner.downcast_ref::() { 88 | Some(o) => { 89 | let v = box try!((self as &Attribute).get(o)); 90 | Ok(v as Box) 91 | }, 92 | None => Err(AttrError::WrongTargetType) 93 | } 94 | } 95 | 96 | fn set(&self, owner: &mut Reflect, new_value: &Reflect) -> AttrResult<()> { 97 | match owner.downcast_mut::() { 98 | Some(o) => { 99 | match new_value.downcast_ref::() { 100 | Some(x) => Ok(try!((self as &Attribute).set(o, x.clone()))), 101 | None => Err(AttrError::WrongValueType) 102 | } 103 | }, 104 | None => Err(AttrError::WrongTargetType) 105 | } 106 | } 107 | 108 | fn type_info(&self) -> &'static Type<'static> { 109 | GetType::of::() as &Type<'static> 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /reflect/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(phase, macro_rules)] 2 | #![allow(missing_copy_implementations)] 3 | 4 | extern crate phf; 5 | #[phase(plugin)] 6 | extern crate phf_mac; 7 | 8 | #[doc(inline)] 9 | pub use attributes::{Attribute, AnyAttribute, FieldAttribute, OwnerAttribute, AttrResult, AttrError, AttributeMap}; 10 | 11 | #[doc(inline)] 12 | pub use type_info::{TypeInfo, GetTypeInfo, TypeInfoFor, Type, GetType}; 13 | 14 | #[doc(inline)] 15 | pub use reflect::{Reflect, ReflectRefExt, ReflectMutRefExt}; 16 | 17 | pub mod attributes; 18 | pub mod type_info; 19 | pub mod reflect; 20 | pub mod types; 21 | -------------------------------------------------------------------------------- /reflect/src/reflect.rs: -------------------------------------------------------------------------------- 1 | use type_info::{Type, GetType, GetTypeInfo}; 2 | use attributes::{AttrResult, AttrError}; 3 | use std::any::{Any, AnyRefExt, AnyMutRefExt}; 4 | 5 | /// Implement this trait to allow reflection over a type. For static types, 6 | /// it is usually simpler to implement the trait `GetTypeInfo`. 7 | /// When using the `reflect_mac` crate, this can be implemented automatically 8 | /// with the `#[reflect]` attribute. 9 | pub trait Reflect: Any { 10 | /// Get the type of the object. 11 | fn type_info(&self) -> &'static Type<'static>; 12 | 13 | /// Get a clone of the attribute by name. 14 | fn get(&self, name: &str) -> AttrResult>; 15 | 16 | /// Set the attribute by name. 17 | fn set(&mut self, name: &str, new_value: &Reflect) -> AttrResult<()>; 18 | 19 | // The following is because we want to reuse the definition of downcast/is from 20 | // AnyRefExt+AnyMutRefExt, but we can't cast between traits. :-( 21 | // In the future this should go away, as component casting is implemented. 22 | #[doc(ignore)] 23 | fn as_any_ref(&self) -> &Any { 24 | self as &Any 25 | } 26 | #[doc(ignore)] 27 | fn as_any_mut_ref(&mut self) -> &mut Any { 28 | self as &mut Any 29 | } 30 | } 31 | 32 | /// Analogous to `AnyRefExt` 33 | pub trait ReflectRefExt<'a> { 34 | fn is(self) -> bool; 35 | fn downcast_ref(self) -> Option<&'a T>; 36 | } 37 | 38 | /// Analogues to `AnyMutRefExt` 39 | pub trait ReflectMutRefExt<'a> { 40 | fn downcast_mut(self) -> Option<&'a mut T>; 41 | } 42 | 43 | impl<'a> ReflectRefExt<'a> for &'a Reflect { 44 | fn is(self) -> bool { 45 | self.as_any_ref().is::() 46 | } 47 | 48 | fn downcast_ref(self) -> Option<&'a T> { 49 | self.as_any_ref().downcast_ref::() 50 | } 51 | } 52 | 53 | impl<'a> ReflectMutRefExt<'a> for &'a mut Reflect { 54 | fn downcast_mut(self) -> Option<&'a mut T> { 55 | self.as_any_mut_ref().downcast_mut::() 56 | } 57 | } 58 | 59 | impl<'a, T> Reflect for T 60 | where T: GetTypeInfo + 'static 61 | { 62 | fn type_info(&self) -> &'static Type<'static> { 63 | let t = GetType::of::(); 64 | t as &'static Type<'static> 65 | } 66 | 67 | fn get(&self, name: &str) -> AttrResult> { 68 | let ti = GetType::of::(); 69 | match ti.attributes.get(name) { 70 | Some(attrfn) => (*attrfn)().get(self), 71 | None => Err(AttrError::UnknownAttribute) 72 | } 73 | } 74 | 75 | fn set(&mut self, name: &str, new_value: &Reflect) -> AttrResult<()> { 76 | let ti = GetType::of::(); 77 | match ti.attributes.get(name) { 78 | Some(attrfn) => (*attrfn)().set(self, new_value), 79 | None => Err(AttrError::UnknownAttribute) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /reflect/src/type_info.rs: -------------------------------------------------------------------------------- 1 | use attributes::{OwnerAttribute, AnyAttribute, AttributeMap}; 2 | 3 | /// Static information about a type. 4 | pub struct TypeInfo { 5 | pub name: &'static str, 6 | pub attributes: &'static AttributeMap, 7 | } 8 | 9 | /// Static information about type `T`. This is used to disambiguate 10 | /// `TypeInfo`s when requesting information about a specific compile-time type. 11 | pub struct TypeInfoFor(pub &'static TypeInfo); 12 | 13 | /// Get static information about type `T`. Implement this to get the `Reflect` trait for free. 14 | pub trait GetTypeInfo { 15 | fn get_type_info(_ignored: Option) -> TypeInfoFor; 16 | } 17 | 18 | /// Information about any type. 19 | pub trait Type<'a> { 20 | fn name(&self) -> &'a str; 21 | fn find_attribute(&self, name: &str) -> Option<&'a AnyAttribute>; 22 | } 23 | 24 | /// Get information about any type. 25 | pub struct GetType; 26 | 27 | impl GetType { 28 | pub fn of() -> &'static TypeInfo { 29 | let TypeInfoFor(ti) = GetTypeInfo::get_type_info(None::); 30 | ti 31 | } 32 | } 33 | 34 | impl Type<'static> for TypeInfo { 35 | fn name(&self) -> &'static str { 36 | self.name 37 | } 38 | 39 | fn find_attribute(&self, name: &str) -> Option<&'static AnyAttribute> { 40 | match self.attributes.get(name) { 41 | Some(attrfn) => { 42 | let attr = (*attrfn)(); 43 | Some(attr) 44 | }, 45 | None => None 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /reflect/src/types.rs: -------------------------------------------------------------------------------- 1 | use type_info::{TypeInfo, TypeInfoFor, GetTypeInfo}; 2 | use phf; 3 | 4 | macro_rules! reflection_for { 5 | ($ty:ty, $name:expr) => { 6 | impl GetTypeInfo for $ty { 7 | fn get_type_info(_: Option<$ty>) -> TypeInfoFor<$ty> { 8 | static TYPE_INFO: TypeInfo = TypeInfo { 9 | name: $name, 10 | attributes: &phf_map!() 11 | }; 12 | TypeInfoFor(&TYPE_INFO) 13 | } 14 | } 15 | } 16 | } 17 | 18 | reflection_for!(i8, "i8"); 19 | reflection_for!(i16, "i16"); 20 | reflection_for!(i32, "i32"); 21 | reflection_for!(i64, "i64"); 22 | reflection_for!(u8, "u8"); 23 | reflection_for!(u16, "u16"); 24 | reflection_for!(u32, "u32"); 25 | reflection_for!(u64, "u64"); 26 | 27 | reflection_for!(char, "char"); 28 | reflection_for!(String, "String"); 29 | 30 | reflection_for!(f32, "f32"); 31 | reflection_for!(f64, "f64"); 32 | -------------------------------------------------------------------------------- /reflect/tests/test_reflect.rs: -------------------------------------------------------------------------------- 1 | use reflect::{GetType, Type, Reflect, ReflectRefExt}; 2 | use std::default::Default; 3 | use std::borrow::ToOwned; 4 | 5 | #[reflect] 6 | struct Foo { 7 | foo: i32, 8 | bar: String, 9 | } 10 | 11 | impl Default for Foo { 12 | fn default() -> Foo { 13 | Foo { 14 | foo: 123, 15 | bar: "Hello, World!".to_owned() 16 | } 17 | } 18 | } 19 | 20 | #[test] 21 | fn get_name_of_type() { 22 | let t = GetType::of::(); 23 | assert!(t.name() == "Foo"); 24 | } 25 | 26 | #[test] 27 | fn get_type_of_attribute() { 28 | let t = GetType::of::(); 29 | let field = t.find_attribute("foo"); 30 | match field { 31 | Some(f) => assert!(f.type_info().name() == GetType::of::().name()), 32 | None => assert!(false, "Member 'foo' did not exist.") 33 | } 34 | } 35 | 36 | #[test] 37 | fn get_member_of_foo() { 38 | let foo: Foo = Default::default(); 39 | let v = foo.get("foo"); 40 | match v { 41 | Ok(b) => match (*b).downcast_ref::() { 42 | Some(n) => assert!(*n == 123), 43 | None => assert!(false, "Member was not i32") 44 | }, 45 | Err(_) => assert!(false, "Member did not exist") 46 | }; 47 | } 48 | 49 | #[test] 50 | fn set_member_of_foo() { 51 | let mut foo: Foo = Default::default(); 52 | let new_value: i32 = 456; 53 | let v = foo.set("foo", &new_value); 54 | match v { 55 | Ok(_) => assert!(foo.foo == new_value), 56 | Err(_) => assert!(false, "Could not set member!"), 57 | } 58 | } 59 | 60 | #[test] 61 | fn set_nonexisting_member_should_fail() { 62 | let mut foo: Foo = Default::default(); 63 | let new_value: i32 = 456; 64 | let v = foo.set("bar", &new_value); 65 | match v { 66 | Err(_) => (), 67 | Ok(_) => assert!(false, "It succeeded for some reason.") 68 | } 69 | } 70 | 71 | #[test] 72 | fn set_member_of_wrong_type_should_fail() { 73 | let mut foo: Foo = Default::default(); 74 | let new_value = "Hello, World!".to_owned(); 75 | let v = foo.set("foo", &new_value); 76 | match v { 77 | Err(_) => (), 78 | Ok(_) => assert!(false, "It succeeded for some reason.") 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /reflect/tests/tests.rs: -------------------------------------------------------------------------------- 1 | #![feature(macro_rules)] 2 | #![feature(phase)] 3 | 4 | extern crate reflect; 5 | #[phase(plugin)] 6 | extern crate reflect_mac; 7 | 8 | mod test_reflect; 9 | -------------------------------------------------------------------------------- /reflect_mac/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reflect_mac" 3 | version = "0.0.1" 4 | authors = ["Simon Ask Ulsnes "] 5 | license = "MIT" 6 | 7 | [lib] 8 | name = "reflect_mac" 9 | path = "src/lib.rs" 10 | plugin = true 11 | test = false 12 | 13 | [dependencies] 14 | phf = "*" 15 | phf_mac = "*" 16 | 17 | [[test]] 18 | name = "tests" 19 | -------------------------------------------------------------------------------- /reflect_mac/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin_registrar, phase, quote, macro_rules)] 2 | 3 | //! Reflect compiler plugin 4 | 5 | extern crate rustc; 6 | extern crate syntax; 7 | extern crate phf; 8 | extern crate phf_mac; 9 | 10 | use syntax::ext::base::{ExtCtxt}; 11 | use syntax::codemap::{Span}; 12 | use syntax::ast; 13 | use syntax::ast::{ItemStruct, StructField}; 14 | use syntax::ptr::P; 15 | use syntax::parse::token; 16 | 17 | fn generate_attributes_map( 18 | c: &mut ExtCtxt, 19 | s: Span, 20 | _: &ast::MetaItem, 21 | _: &ast::Item, 22 | fields: &Vec 23 | ) -> P { 24 | use syntax::ext::build::AstBuilder; 25 | 26 | let entries = fields.iter().map(|ref field| { 27 | let ident = field.node.ident().unwrap(); 28 | let key_contents = phf_mac::util::Key::Str(token::get_ident(ident.clone())); 29 | let fn_name = c.ident_of(format!("attr_{}", ident.as_str()).as_slice()); 30 | phf_mac::util::Entry { 31 | key: c.expr_lit(s, ast::LitStr(token::get_ident(ident.clone()), ast::StrStyle::CookedStr)), 32 | key_contents: key_contents, 33 | value: quote_expr!(c, $fn_name as fn()-> &'static ::reflect::AnyAttribute) 34 | } 35 | }).collect::>(); 36 | 37 | let state = phf_mac::util::generate_hash(c, s, entries.as_slice()); 38 | let created_map = phf_mac::util::create_map(c, s, entries, state); 39 | 40 | // Now we have the equivalent of what phf_map! does, but we don't 41 | // want to force the user to add "extern crate phf" to their build, 42 | // so replace ::phf::Map with ::reflect::AttributeMap (which is the same). 43 | let map_expr = created_map.make_expr().unwrap(); 44 | let new_map_expr = match map_expr.node { 45 | ast::ExprStruct(_, ref fields, _) => { 46 | let new_path = c.path_global(s, vec!(c.ident_of("reflect"), c.ident_of("AttributeMap"))); 47 | c.expr_struct(s, new_path, fields.clone()) 48 | }, 49 | _ => c.span_bug(s, "phf_mac::util::create_map did not return an expression to initialize a map.") 50 | }; 51 | 52 | let attributes_initializer = P(new_map_expr); 53 | 54 | quote_item!(c, static ATTRIBUTES: ::reflect::AttributeMap = $attributes_initializer;).unwrap() 55 | } 56 | 57 | fn generate_attribute_info_getter( 58 | c: &mut ExtCtxt, 59 | s: Span, 60 | _: &ast::MetaItem, 61 | struct_item: &ast::Item, 62 | field: &StructField 63 | ) -> P { 64 | use syntax::ext::build::AstBuilder; 65 | 66 | let ident = match field.node.ident() { 67 | Some(i) => i, 68 | None => c.span_bug(s, format!("unnamed field in normal struct").as_slice()) 69 | }; 70 | 71 | let field_ty = field.node.ty.clone(); 72 | let self_ty = struct_item.ident; 73 | let fn_name = c.ident_of(format!("attr_{}", ident.as_str()).as_slice()); 74 | 75 | quote_item!(c, 76 | fn $fn_name() -> &'static ::reflect::AnyAttribute { 77 | struct Attr; 78 | static ATTR: Attr = Attr; 79 | 80 | impl ::reflect::Attribute<$self_ty, $field_ty> for Attr { 81 | fn get(&self, instance: &$self_ty) -> ::reflect::AttrResult<$field_ty> { 82 | Ok(instance.$ident.clone()) 83 | } 84 | fn set(&self, instance: &mut $self_ty, new_value: $field_ty) -> ::reflect::AttrResult<()> { 85 | instance.$ident = new_value; 86 | Ok(()) 87 | } 88 | } 89 | 90 | &ATTR as &::reflect::AnyAttribute 91 | } 92 | ).unwrap() 93 | } 94 | 95 | fn generate_attributes_info_getters( 96 | c: &mut ExtCtxt, 97 | s: Span, 98 | meta_item: &ast::MetaItem, 99 | struct_item: &ast::Item, 100 | fields: &Vec 101 | ) -> Vec> { 102 | use syntax::ext::build::AstBuilder; 103 | 104 | fields.iter().map(|field| { 105 | let fn_item = generate_attribute_info_getter(c, s, meta_item, struct_item, field); 106 | c.stmt_item(s, fn_item) 107 | }).collect::>() 108 | } 109 | 110 | fn generate_get_type_info_fn_impl( 111 | c: &mut ExtCtxt, 112 | s: Span, 113 | meta_item: &ast::MetaItem, 114 | struct_item: &ast::Item, 115 | ) -> P { 116 | use syntax::ext::build::AstBuilder; 117 | 118 | // TODO: Get fully qualified name with all modules etc. 119 | let self_name = token::get_ident(struct_item.ident); 120 | 121 | let fields = match struct_item.node { 122 | ItemStruct(ref struct_def, _) => &struct_def.fields, 123 | _ => c.span_bug(s, format!("Expected struct, got {}", struct_item).as_slice()) 124 | }; 125 | 126 | let mut stmts = generate_attributes_info_getters(c, s, meta_item, struct_item, fields); 127 | let generated_attr_map = generate_attributes_map(c, s, meta_item, struct_item, fields); 128 | stmts.push(c.stmt_item(s, generated_attr_map)); 129 | 130 | let name_expr = c.expr_str(s, self_name); 131 | let type_info_initializer = quote_expr!(c, ::reflect::TypeInfo { 132 | name: $name_expr, 133 | attributes: &ATTRIBUTES, 134 | }); 135 | 136 | let type_info_decl = quote_item!(c, static TYPE_INFO: ::reflect::TypeInfo = $type_info_initializer;).unwrap(); 137 | stmts.push(c.stmt_item(s, type_info_decl)); 138 | let retval = quote_expr!(c, ::reflect::TypeInfoFor(&TYPE_INFO)); 139 | 140 | c.block(s, stmts, Some(retval)) 141 | } 142 | 143 | fn generate_get_type_info_impl_for_struct( 144 | c: &mut ExtCtxt, 145 | s: Span, 146 | meta_item: &ast::MetaItem, 147 | struct_item: &ast::Item, 148 | ) -> P { 149 | let ty = struct_item.ident; 150 | let type_info_for_impl = generate_get_type_info_fn_impl(c, s, meta_item, struct_item); 151 | 152 | quote_item!(c, 153 | impl ::reflect::GetTypeInfo for $ty { 154 | fn get_type_info(_: Option<$ty>) -> ::reflect::TypeInfoFor<$ty> 155 | $type_info_for_impl 156 | } 157 | ).unwrap() 158 | } 159 | 160 | /// Generate code for reflection over a struct. 161 | pub fn reflect_on_struct(context: &mut ExtCtxt, span: Span, meta_item: &ast::MetaItem, item: &ast::Item, push: |P|) { 162 | use syntax::ast::{Item_}; 163 | match &item.node { 164 | &Item_::ItemStruct(_, _) => { 165 | push(generate_get_type_info_impl_for_struct(context, span, meta_item, item)) 166 | }, 167 | _ => { 168 | context.span_bug(span, "reflect_on_struct called with non-struct argument."); 169 | } 170 | }; 171 | } 172 | 173 | /// Generate code for reflection over an enum. 174 | pub fn reflect_on_enum(context: &mut ExtCtxt, span: Span, meta_item: &ast::MetaItem, item: &ast::Item, push: |P|) { 175 | context.span_bug(span, "reflect_on_enum NIY"); 176 | } 177 | 178 | /// Generate code for reflection over a newtype. 179 | pub fn reflect_on_ty(context: &mut ExtCtxt, span: Span, meta_item: &ast::MetaItem, item: &ast::Item, push: |P|) { 180 | context.span_bug(span, "reflect_on_ty NIY"); 181 | } 182 | 183 | fn expand_reflect(context: &mut ExtCtxt, span: Span, meta_item: &ast::MetaItem, item: &ast::Item, push: |P| ) { 184 | use syntax::ast::{Item_}; 185 | match &item.node { 186 | &Item_::ItemStruct(_, _) => { 187 | reflect_on_struct(context, span, meta_item, item, push); 188 | }, 189 | &Item_::ItemEnum(_, _) => { 190 | reflect_on_enum(context, span, meta_item, item, push); 191 | }, 192 | &Item_::ItemTy(_, _) => { 193 | reflect_on_ty(context, span, meta_item, item, push); 194 | } 195 | _ => { 196 | context.span_err(span, "#[reflect] attribute can only be used on structs and enums.") 197 | } 198 | }; 199 | } 200 | 201 | #[plugin_registrar] 202 | pub fn registrar(reg: &mut rustc::plugin::Registry) { 203 | use syntax::ext::base; 204 | use syntax::parse::token::intern; 205 | 206 | let interned_name = intern("reflect"); 207 | let decorator = base::SyntaxExtension::Decorator(box expand_reflect); 208 | reg.register_syntax_extension(interned_name, decorator); 209 | } 210 | -------------------------------------------------------------------------------- /reflect_mac/tests/tests.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonask/rust-reflect/1824a5014e1f3f753b8a2aa1a3ceac07cd86f942/reflect_mac/tests/tests.rs --------------------------------------------------------------------------------