1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
//! An implementation of detailed type information and reflection.
//!
//! This library provides simple access to type information at runtime, as well as the ability to
//! manipulate data whose type is not statically known.
//!
//! # Type information
//!
//! By deriving the `TypeInfo` trait, you get access to type information about the type at runtime.
//!
//! ```
//! #![feature(const_type_id)]
//!
//! extern crate type_info;
//! #[macro_use]
//! extern crate type_info_derive;
//!
//! use type_info::TypeInfo;
//!
//! #[derive(TypeInfo)]
//! struct Person {
//!     name: String,
//!     age: u32,
//! }
//!
//! fn main() {
//!     let ty = Person::TYPE;
//!
//!     assert_eq!("Person", ty.ident);
//!     assert_eq!(vec!["name", "age"], ty.fields().iter().map(|f| f.ident.unwrap()).collect::<Vec<_>>());
//! }
//! ```
//!
//! # Static dispatch
//!
//! This example shows how to use the main `TypeInfo` trait with generic parameters (i.e. not trait
//! objects):
//!
//! ```
//! # #![feature(const_type_id)]
//! #
//! # extern crate type_info;
//! # #[macro_use]
//! # extern crate type_info_derive;
//! #
//! # #[derive(TypeInfo)]
//! # struct Person {
//! #     name: String,
//! #     age: u32,
//! # }
//! // Person is defined like in the above example...
//!
//! // A function that can take any type that has a field called "name" of type String.
//! fn add_smith_to_name<A>(anything: &mut A) where A: type_info::TypeInfo {
//!     let name = anything.field_mut::<String>(type_info::FieldId::Named("name")).unwrap();
//!     name.push_str(" Smith");
//! }
//!
//! fn main() {
//!     let mut person = Person {
//!         name: "Lisa".to_owned(),
//!         age: 23,
//!     };
//!
//!     add_smith_to_name(&mut person);
//!
//!     assert_eq!("Lisa Smith", person.name.as_str());
//! }
//! ```
//!
//! # Dynamic dispatch
//!
//! This example shows how to use the `DynamicTypeInfo` trait when you don't want to introduce a
//! type parameter to specialize a function, but instead prefer to use a trait object:
//!
//! ```
//! # #![feature(const_type_id)]
//! #
//! # extern crate type_info;
//! # #[macro_use]
//! # extern crate type_info_derive;
//! #
//! # #[derive(TypeInfo)]
//! # struct Person {
//! #     name: String,
//! #     age: u32,
//! # }
//! // Person is defined like in the above example...
//!
//! // A function that can take any type that has a field called "name" of type String.
//! fn add_smith_to_name(anything: &mut type_info::DynamicTypeInfo) {
//!     let field = anything.field_any_mut(type_info::FieldId::Named("name")).unwrap();
//!     let name = field.downcast_mut::<String>().unwrap();
//!     name.push_str(" Smith");
//! }
//!
//! fn main() {
//!     let mut person = Person {
//!         name: "Lisa".to_owned(),
//!         age: 23,
//!     };
//!
//!     add_smith_to_name(&mut person);
//!
//!     assert_eq!("Lisa Smith", person.name.as_str());
//! }
//! ```
#![feature(const_type_id)]
#![feature(specialization)]
#![deny(
    missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
    trivial_numeric_casts, unsafe_code, unused_import_braces, unused_qualifications
)]

use std::any;
use std::fmt;

/// A globally unique identifier for a type.
pub type TypeId = any::TypeId;

/// A locally unique identifier for a field within a certain type.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub enum FieldId<'a> {
    /// An unnamed field with the specified index in a tuple-like struct; e.g. the `0` in `foo.0`.
    Unnamed(usize),
    /// A named field in a struct, e.g. the `name` in `foo.name`.
    Named(&'a str),
}

/// A type that has compile-time static type information associated with it.
pub trait TypeInfo: DynamicTypeInfo {
    /// The constant statically known type information for this type.
    const TYPE: Type;

    /// Get a reference to the value of a field on this type with the given field id.
    ///
    /// This method will return the current value of the given field if possible, or `None` if the
    /// given field does not exist or does not have a type matching the supplied type.
    fn field<A>(&self, _id: FieldId) -> Option<&A>
    where
        A: any::Any,
    {
        None
    }

    /// Get a mutable reference to the value of a field on this type with the given field id.
    ///
    /// This method will return the current value of the given field if possible, or `None` if the
    /// given field does not exist or does not have a type matching the supplied type.
    fn field_mut<A>(&mut self, _id: FieldId) -> Option<&mut A>
    where
        A: any::Any,
    {
        None
    }
}

/// A type that has compile-time dynamic type information associated with it.
///
/// This trait is built to be compatible with being a trait object.
pub trait DynamicTypeInfo {
    /// The dynamic statically known type information for this type.
    fn type_ref(&self) -> &'static Type;

    /// Get the id of the currently active variant of this type, or `None` if the type is not
    /// an `enum`.
    fn variant(&self) -> Option<&str> {
        None
    }

    /// Get a dynamic reference to the value of a field on this type with the given field id.
    ///
    /// This method will return the current value of the given field if possible, or `None` if the
    /// given field does not exist or does not have a type matching the supplied type.
    fn field_any(&self, _id: FieldId) -> Option<&any::Any> {
        None
    }

    /// Get a mutable dynamic reference to the value of a field on this type with the given field id.
    ///
    /// This method will return the current value of the given field if possible, or `None` if the
    /// given field does not exist or does not have a type matching the supplied type.
    fn field_any_mut(&mut self, _id: FieldId) -> Option<&mut any::Any> {
        None
    }
}

/// A trait that is implemented for every type to conditionally determine whether it exposes type
/// information.
pub trait TryTypeInfo {
    /// The constant statically known type information for this type, or `None` if the type does not
    /// implement `TypeInfo`.
    const TRY_TYPE: Option<&'static Type>;
}

impl<T> TryTypeInfo for T {
    default const TRY_TYPE: Option<&'static Type> = None;
}

impl<T> TryTypeInfo for T
where
    T: TypeInfo,
{
    const TRY_TYPE: Option<&'static Type> = Some(&T::TYPE);
}

/// Type information for a type that implements `TypeInfo`.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Type {
    /// The globally unique identifier for this type.
    pub id: TypeId,

    /// The module in which this type was defined.
    /// This is using unrooted syntax à la `foo::bar`.
    pub module: &'static str,

    /// The identifier of this type within its module.
    pub ident: &'static str,

    /// Additional data about this type definition.
    pub data: Data,
}

/// Data associated with type information.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Data {
    /// The associated type is a primitive type.
    Primitive,
    /// The associated type is a `struct`.
    Struct(DataStruct),
    /// The associated type is an `enum`.
    Enum(DataEnum),
    /// The associated type is an `union`.
    Union(DataUnion),
}

/// Data associated with `struct` type information.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DataStruct {
    /// The fields that this `struct` consists of.
    pub fields: Fields,
}

/// Data associated with `enum` type information.
#[allow(missing_copy_implementations)]
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DataEnum {
    /// The variants that this `enum` consists of.
    pub variants: &'static [Variant],
}

/// Data associated with `union` type information.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DataUnion {
    /// The fields that this `union` consists of.
    pub fields: FieldsNamed,
}

/// A specific `enum` variant.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Variant {
    /// The identifier of the enum variant.
    pub ident: &'static str,
    /// The fields that are associated with a particular `enum` variant.
    pub fields: Fields,
}

/// A set of fields associated with a type or `enum` variant.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Fields {
    /// A set of named fields.
    Named(FieldsNamed),
    /// A set of index-addressed fields
    Unnamed(FieldsUnnamed),
    /// The empty set of fields, applicable to unit structs or enum variants.
    Unit,
}

/// A set of named fields associated with a type or `enum` variant.
#[allow(missing_copy_implementations)]
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FieldsNamed {
    /// The related set of named fields.
    pub named: &'static [Field],
}

/// A set of unnamed fields associated with a type or `enum` variant.
#[allow(missing_copy_implementations)]
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FieldsUnnamed {
    /// The related set of unnamed fields.
    pub unnamed: &'static [Field],
}

/// A field that is associated with a type or `enum` variant.
#[allow(missing_copy_implementations)]
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Field {
    /// The type or variant local unique identifier for the field.
    pub id: FieldId<'static>,
    /// The field's identifier, if it is named.
    pub ident: Option<&'static str>,
    /// The type of the field, if it has any associated `TypeInfo`.
    pub ty: Option<&'static Type>,
}

impl Type {
    /// Convenience method for getting all of the struct fields of this type.
    pub fn fields(&self) -> &'static [Field] {
        match self.data {
            Data::Struct(DataStruct { ref fields, .. }) => fields.fields(),
            _ => &[],
        }
    }

    /// Convenience method for getting all of the enum variants of this type.
    pub fn variants(&self) -> &'static [Variant] {
        match self.data {
            Data::Enum(DataEnum { ref variants, .. }) => variants,
            _ => &[],
        }
    }
}

impl Fields {
    /// Convenience method for getting all of the fields, ignoring whether they are named or
    /// unnamed.
    pub fn fields(&self) -> &'static [Field] {
        match *self {
            Fields::Unit => &[],
            Fields::Named(FieldsNamed { named, .. }) => named,
            Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed,
        }
    }
}

impl<'a> fmt::Display for FieldId<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FieldId::Unnamed(idx) => idx.fmt(f),
            FieldId::Named(name) => name.fmt(f),
        }
    }
}

macro_rules! impl_primitive {
    ($t:ty) => {
        impl TypeInfo for $t {
            const TYPE: Type = Type {
                id: TypeId::of::<$t>(),
                module: "",
                ident: stringify!($t),
                data: Data::Primitive,
            };
        }

        impl DynamicTypeInfo for $t {
            fn type_ref(&self) -> &'static Type {
                &<Self as TypeInfo>::TYPE
            }
        }
    };
}

impl_primitive!(u8);
impl_primitive!(u16);
impl_primitive!(u32);
impl_primitive!(u64);
impl_primitive!(usize);

impl_primitive!(i8);
impl_primitive!(i16);
impl_primitive!(i32);
impl_primitive!(i64);
impl_primitive!(isize);

impl_primitive!(f32);
impl_primitive!(f64);

impl_primitive!(bool);

impl_primitive!(char);