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);