type_info/
lib.rs

1//! An implementation of detailed type information and reflection.
2//!
3//! This library provides simple access to type information at runtime, as well as the ability to
4//! manipulate data whose type is not statically known.
5//!
6//! # Type information
7//!
8//! By deriving the `TypeInfo` trait, you get access to type information about the type at runtime.
9//!
10//! ```
11//! #![feature(const_type_id)]
12//!
13//! extern crate type_info;
14//! #[macro_use]
15//! extern crate type_info_derive;
16//!
17//! use type_info::TypeInfo;
18//!
19//! #[derive(TypeInfo)]
20//! struct Person {
21//!     name: String,
22//!     age: u32,
23//! }
24//!
25//! fn main() {
26//!     let ty = Person::TYPE;
27//!
28//!     assert_eq!("Person", ty.ident);
29//!     assert_eq!(vec!["name", "age"], ty.fields().iter().map(|f| f.ident.unwrap()).collect::<Vec<_>>());
30//! }
31//! ```
32//!
33//! # Static dispatch
34//!
35//! This example shows how to use the main `TypeInfo` trait with generic parameters (i.e. not trait
36//! objects):
37//!
38//! ```
39//! # #![feature(const_type_id)]
40//! #
41//! # extern crate type_info;
42//! # #[macro_use]
43//! # extern crate type_info_derive;
44//! #
45//! # #[derive(TypeInfo)]
46//! # struct Person {
47//! #     name: String,
48//! #     age: u32,
49//! # }
50//! // Person is defined like in the above example...
51//!
52//! // A function that can take any type that has a field called "name" of type String.
53//! fn add_smith_to_name<A>(anything: &mut A) where A: type_info::TypeInfo {
54//!     let name = anything.field_mut::<String>(type_info::FieldId::Named("name")).unwrap();
55//!     name.push_str(" Smith");
56//! }
57//!
58//! fn main() {
59//!     let mut person = Person {
60//!         name: "Lisa".to_owned(),
61//!         age: 23,
62//!     };
63//!
64//!     add_smith_to_name(&mut person);
65//!
66//!     assert_eq!("Lisa Smith", person.name.as_str());
67//! }
68//! ```
69//!
70//! # Dynamic dispatch
71//!
72//! This example shows how to use the `DynamicTypeInfo` trait when you don't want to introduce a
73//! type parameter to specialize a function, but instead prefer to use a trait object:
74//!
75//! ```
76//! # #![feature(const_type_id)]
77//! #
78//! # extern crate type_info;
79//! # #[macro_use]
80//! # extern crate type_info_derive;
81//! #
82//! # #[derive(TypeInfo)]
83//! # struct Person {
84//! #     name: String,
85//! #     age: u32,
86//! # }
87//! // Person is defined like in the above example...
88//!
89//! // A function that can take any type that has a field called "name" of type String.
90//! fn add_smith_to_name(anything: &mut type_info::DynamicTypeInfo) {
91//!     let field = anything.field_any_mut(type_info::FieldId::Named("name")).unwrap();
92//!     let name = field.downcast_mut::<String>().unwrap();
93//!     name.push_str(" Smith");
94//! }
95//!
96//! fn main() {
97//!     let mut person = Person {
98//!         name: "Lisa".to_owned(),
99//!         age: 23,
100//!     };
101//!
102//!     add_smith_to_name(&mut person);
103//!
104//!     assert_eq!("Lisa Smith", person.name.as_str());
105//! }
106//! ```
107#![feature(const_type_id)]
108#![feature(specialization)]
109#![deny(
110    missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
111    trivial_numeric_casts, unsafe_code, unused_import_braces, unused_qualifications
112)]
113
114use std::any;
115use std::fmt;
116
117/// A globally unique identifier for a type.
118pub type TypeId = any::TypeId;
119
120/// A locally unique identifier for a field within a certain type.
121#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
122pub enum FieldId<'a> {
123    /// An unnamed field with the specified index in a tuple-like struct; e.g. the `0` in `foo.0`.
124    Unnamed(usize),
125    /// A named field in a struct, e.g. the `name` in `foo.name`.
126    Named(&'a str),
127}
128
129/// A type that has compile-time static type information associated with it.
130pub trait TypeInfo: DynamicTypeInfo {
131    /// The constant statically known type information for this type.
132    const TYPE: Type;
133
134    /// Get a reference to the value of a field on this type with the given field id.
135    ///
136    /// This method will return the current value of the given field if possible, or `None` if the
137    /// given field does not exist or does not have a type matching the supplied type.
138    fn field<A>(&self, _id: FieldId) -> Option<&A>
139    where
140        A: any::Any,
141    {
142        None
143    }
144
145    /// Get a mutable reference to the value of a field on this type with the given field id.
146    ///
147    /// This method will return the current value of the given field if possible, or `None` if the
148    /// given field does not exist or does not have a type matching the supplied type.
149    fn field_mut<A>(&mut self, _id: FieldId) -> Option<&mut A>
150    where
151        A: any::Any,
152    {
153        None
154    }
155}
156
157/// A type that has compile-time dynamic type information associated with it.
158///
159/// This trait is built to be compatible with being a trait object.
160pub trait DynamicTypeInfo {
161    /// The dynamic statically known type information for this type.
162    fn type_ref(&self) -> &'static Type;
163
164    /// Get the id of the currently active variant of this type, or `None` if the type is not
165    /// an `enum`.
166    fn variant(&self) -> Option<&str> {
167        None
168    }
169
170    /// Get a dynamic reference to the value of a field on this type with the given field id.
171    ///
172    /// This method will return the current value of the given field if possible, or `None` if the
173    /// given field does not exist or does not have a type matching the supplied type.
174    fn field_any(&self, _id: FieldId) -> Option<&any::Any> {
175        None
176    }
177
178    /// Get a mutable dynamic reference to the value of a field on this type with the given field id.
179    ///
180    /// This method will return the current value of the given field if possible, or `None` if the
181    /// given field does not exist or does not have a type matching the supplied type.
182    fn field_any_mut(&mut self, _id: FieldId) -> Option<&mut any::Any> {
183        None
184    }
185}
186
187/// A trait that is implemented for every type to conditionally determine whether it exposes type
188/// information.
189pub trait TryTypeInfo {
190    /// The constant statically known type information for this type, or `None` if the type does not
191    /// implement `TypeInfo`.
192    const TRY_TYPE: Option<&'static Type>;
193}
194
195impl<T> TryTypeInfo for T {
196    default const TRY_TYPE: Option<&'static Type> = None;
197}
198
199impl<T> TryTypeInfo for T
200where
201    T: TypeInfo,
202{
203    const TRY_TYPE: Option<&'static Type> = Some(&T::TYPE);
204}
205
206/// Type information for a type that implements `TypeInfo`.
207#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
208pub struct Type {
209    /// The globally unique identifier for this type.
210    pub id: TypeId,
211
212    /// The module in which this type was defined.
213    /// This is using unrooted syntax à la `foo::bar`.
214    pub module: &'static str,
215
216    /// The identifier of this type within its module.
217    pub ident: &'static str,
218
219    /// Additional data about this type definition.
220    pub data: Data,
221}
222
223/// Data associated with type information.
224#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
225pub enum Data {
226    /// The associated type is a primitive type.
227    Primitive,
228    /// The associated type is a `struct`.
229    Struct(DataStruct),
230    /// The associated type is an `enum`.
231    Enum(DataEnum),
232    /// The associated type is an `union`.
233    Union(DataUnion),
234}
235
236/// Data associated with `struct` type information.
237#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
238pub struct DataStruct {
239    /// The fields that this `struct` consists of.
240    pub fields: Fields,
241}
242
243/// Data associated with `enum` type information.
244#[allow(missing_copy_implementations)]
245#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
246pub struct DataEnum {
247    /// The variants that this `enum` consists of.
248    pub variants: &'static [Variant],
249}
250
251/// Data associated with `union` type information.
252#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
253pub struct DataUnion {
254    /// The fields that this `union` consists of.
255    pub fields: FieldsNamed,
256}
257
258/// A specific `enum` variant.
259#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
260pub struct Variant {
261    /// The identifier of the enum variant.
262    pub ident: &'static str,
263    /// The fields that are associated with a particular `enum` variant.
264    pub fields: Fields,
265}
266
267/// A set of fields associated with a type or `enum` variant.
268#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
269pub enum Fields {
270    /// A set of named fields.
271    Named(FieldsNamed),
272    /// A set of index-addressed fields
273    Unnamed(FieldsUnnamed),
274    /// The empty set of fields, applicable to unit structs or enum variants.
275    Unit,
276}
277
278/// A set of named fields associated with a type or `enum` variant.
279#[allow(missing_copy_implementations)]
280#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
281pub struct FieldsNamed {
282    /// The related set of named fields.
283    pub named: &'static [Field],
284}
285
286/// A set of unnamed fields associated with a type or `enum` variant.
287#[allow(missing_copy_implementations)]
288#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
289pub struct FieldsUnnamed {
290    /// The related set of unnamed fields.
291    pub unnamed: &'static [Field],
292}
293
294/// A field that is associated with a type or `enum` variant.
295#[allow(missing_copy_implementations)]
296#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
297pub struct Field {
298    /// The type or variant local unique identifier for the field.
299    pub id: FieldId<'static>,
300    /// The field's identifier, if it is named.
301    pub ident: Option<&'static str>,
302    /// The type of the field, if it has any associated `TypeInfo`.
303    pub ty: Option<&'static Type>,
304}
305
306impl Type {
307    /// Convenience method for getting all of the struct fields of this type.
308    pub fn fields(&self) -> &'static [Field] {
309        match self.data {
310            Data::Struct(DataStruct { ref fields, .. }) => fields.fields(),
311            _ => &[],
312        }
313    }
314
315    /// Convenience method for getting all of the enum variants of this type.
316    pub fn variants(&self) -> &'static [Variant] {
317        match self.data {
318            Data::Enum(DataEnum { ref variants, .. }) => variants,
319            _ => &[],
320        }
321    }
322}
323
324impl Fields {
325    /// Convenience method for getting all of the fields, ignoring whether they are named or
326    /// unnamed.
327    pub fn fields(&self) -> &'static [Field] {
328        match *self {
329            Fields::Unit => &[],
330            Fields::Named(FieldsNamed { named, .. }) => named,
331            Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed,
332        }
333    }
334}
335
336impl<'a> fmt::Display for FieldId<'a> {
337    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
338        match *self {
339            FieldId::Unnamed(idx) => idx.fmt(f),
340            FieldId::Named(name) => name.fmt(f),
341        }
342    }
343}
344
345macro_rules! impl_primitive {
346    ($t:ty) => {
347        impl TypeInfo for $t {
348            const TYPE: Type = Type {
349                id: TypeId::of::<$t>(),
350                module: "",
351                ident: stringify!($t),
352                data: Data::Primitive,
353            };
354        }
355
356        impl DynamicTypeInfo for $t {
357            fn type_ref(&self) -> &'static Type {
358                &<Self as TypeInfo>::TYPE
359            }
360        }
361    };
362}
363
364impl_primitive!(u8);
365impl_primitive!(u16);
366impl_primitive!(u32);
367impl_primitive!(u64);
368impl_primitive!(usize);
369
370impl_primitive!(i8);
371impl_primitive!(i16);
372impl_primitive!(i32);
373impl_primitive!(i64);
374impl_primitive!(isize);
375
376impl_primitive!(f32);
377impl_primitive!(f64);
378
379impl_primitive!(bool);
380
381impl_primitive!(char);