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
#![allow(incomplete_features)]
#![feature(generic_associated_types)]
//! **WARNING:** This crate requires a nightly compiler and
//! `generic_associated_types`.
//!
//! This crate provides a macro to create a type level record, in which values
//! can be fetched by their types. The types of the things within the record are
//! specified by an inputted `Mapping`.
//!
//! Example usage:
//!
//! ```rust
//! #![allow(incomplete_features)]
//! #![feature(generic_associated_types)]
//! use type_record::{record, Mapping};
//! struct Thing(u8);
//! struct OtherThing(i64);
//! // This creates a type level record, by the name of "Record",
//! // with component types `Thing` and `OtherThing`.
//! record! {
//!     Record {
//!         Thing,
//!         OtherThing,
//!     }
//! }
//!
//! use std::collections::HashMap;
//! struct HashMapping;
//! impl Mapping for HashMapping {
//!     type To<X> = HashMap<usize, X>;
//!     // This type is the arguments inputted into the create function.
//!     type Arguments = ();
//!     // This create function is called for each type within the record.
//!     fn create<X>(_: &Self::Arguments) -> Self::To<X> {
//!         HashMap::new()
//!     }
//! }
//!
//! #[test]
//! fn test() {
//!     // Creates a record, using the `HashMapping` mapping, and arguments `()`.
//!     let mut record = Record::<HashMapping>::new(());
//!     // Gets a mutable reference to the `HashMap<usize, Thing>` within the record.
//!     record.get_mut().insert(0, Thing(16));
//!     // Gets a `HashMap<usize, Thing>` and finds the inserted `Thing`.
//!     assert_eq!(16, record.get::<Thing>()[&0].0);
//!     record
//!         .get_mut()
//!         .insert(18, OtherThing(1024));
//!     assert_eq!(1024, record.get::<OtherThing>()[&18].0);
//! }
//! ```

/// A mapping from a type to another type, with a function for creation.
pub trait Mapping {
    /// This type function controls what the record types are.
    ///
    /// If this is `type To<X> = Vec<X>`, then the record will consist of
    /// vectors of the component types.
    type To<X>;
    /// The arguments provided to the `create` function.
    type Arguments;
    /// This function creates something of `To<X>` given the arguments.
    /// In a record, this would be called for each component type.
    fn create<X>(arguments: &Self::Arguments) -> Self::To<X>;
}

/// This type exists for macro expansion. Do not use.
pub struct _EmptyMapping;

impl Mapping for _EmptyMapping {
    type To<X> = ();
    type Arguments = ();
    fn create<X>(_: &Self::Arguments) -> Self::To<X> {}
}
/// This type exists for macro expansion. Do not use.
pub trait _RecordType<R>
where
    Self: Sized, {
    type Record<X: Mapping>;
    fn get<X: Mapping>(record: &Self::Record<X>) -> &X::To<Self>;
    fn get_mut<X: Mapping>(record: &mut Self::Record<X>) -> &mut X::To<Self>;
}
/// This type exists for macro expansion. Do not use.
pub trait _Record<X: Mapping, U, T: _RecordType<U>> {
    fn _get(&self) -> &X::To<T>;
    fn _get_mut(&mut self) -> &mut X::To<T>;
}
impl<X: Mapping, U, T: _RecordType<U>> _Record<X, U, T> for T::Record<X> {
    #[inline]
    fn _get(&self) -> &X::To<T> {
        T::get::<X>(self)
    }
    #[inline]
    fn _get_mut(&mut self) -> &mut X::To<T> {
        T::get_mut::<X>(self)
    }
}

#[macro_export]
/// The macro to create a type level record.
/// Always generates a `struct` with name `$record_name`, with a single generic
/// argument, which is the Mapping that controls what the record contains.
macro_rules! record {
    ($record_name:ident { $($record_contents:ident),+ $(,)? }) => {
        #[allow(non_snake_case)]
        pub struct $record_name<X: ::type_record::Mapping> {
            $($record_contents: X::To<$record_contents>,)+
        }
        $(impl ::type_record::_RecordType<$record_name<::type_record::_EmptyMapping>> for $record_contents {
            type Record<X: ::type_record::Mapping> = $record_name<X>;
            #[inline]
            fn get<X: ::type_record::Mapping>(record: &$record_name<X>) -> &X::To<Self> {
                &record.$record_contents
            }
            #[inline]
            fn get_mut<X: ::type_record::Mapping>(record: &mut $record_name<X>) -> &mut X::To<Self> {
                &mut record.$record_contents
            }
        })+
        impl<X: ::type_record::Mapping> $record_name<X> {
            #[inline]
            #[doc = "Creates a new record using the arguments provided."]
            pub fn new(arguments: X::Arguments) -> Self {
                $record_name {
                    $($record_contents: X::create::<$record_contents>(&arguments),)+
                }
            }
            #[inline]
            #[doc = "Gets an immutable reference to the value with key `T`."]
            pub fn get<
                T: ::type_record::_RecordType<$record_name<::type_record::_EmptyMapping>>
            >(&self) -> &X::To<T> where Self: ::type_record::_Record<X, $record_name<::type_record::_EmptyMapping>, T> {
                <Self as ::type_record::_Record<X, $record_name<::type_record::_EmptyMapping>, T>>::_get(self)
            }
            #[inline]
            #[doc = "Gets a mutable reference to the value with key `T`."]
            pub fn get_mut<
                T: ::type_record::_RecordType<$record_name<::type_record::_EmptyMapping>>
            >(&mut self) -> &mut X::To<T> where Self: ::type_record::_Record<X, $record_name<::type_record::_EmptyMapping>, T> {
                <Self as ::type_record::_Record<X, $record_name<::type_record::_EmptyMapping>, T>>::_get_mut(self)
            }
        }
    };
}