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
use super::{Sealed, Ty, TyEnd};

use core::any::Any;

/// The Contains trait marks a map as containing at least one instance of a given type that can be accessed
///
/// ```
/// use typemap_core::{typemap, Contains};
/// fn requires_u32<Opts: Contains<u32>>(opts: &Opts) {
///     println!("{}", opts.get::<u32>());
/// }
///
/// requires_u32(&typemap!(u32 = 42u32));
/// ```
///
/// On nightly, this trait functions as intended with the help of an unstable feature.
/// Unfortunately, it cannot be implemented as intended on stable at the moment,
/// so it is given a blanket implementation to ensure that bounds that work on nightly
/// do not cause errors on stable.
// Each implementation of Contains corresponds with an implementation of TypeMapGet
#[cfg_attr(nightly, marker)]
pub trait Contains<T>: TypeMapGet {}

// TODO: more fleshed out trait documentation
/// The TypeMapGet trait allows for obtaining values of types from a typemap.
pub trait TypeMapGet: Sealed {
    /// Attempts to obtain a value of a given type from the map.
    ///
    /// This is mainly intended for the case where you don't require a type to be present,
    /// but would like to act on it if it is.
    /// Returns [`None`] if the type is not present in the map.
    /// On nightly, this should only occur if [`Contains<T>`] is not implemented.
    ///
    /// ```
    /// use typemap_core::{typemap, TypeMapGet};
    ///
    /// let map = typemap!(u32 = 23u32);
    ///
    /// assert_eq!(map.try_get::<u32>(), Some(&23u32));
    /// assert_eq!(map.try_get::<u128>(), None);
    /// ```
    fn try_get<T: 'static>(&self) -> Option<&T>;

    /// Obtains a value of a given type from the map.
    ///
    /// On nightly, you can only call this method if the type is actually present.
    /// (i.e. the map implements implements [`Contains<T>`])
    ///
    /// # Panics
    ///
    /// This function panics if [`try_get`] would return [`None`].
    /// This should only be possible on stable,
    /// so you can weed out potential panics by occasionally checking against nightly.
    ///
    /// ```
    /// use typemap_core::{typemap, TypeMapGet};
    ///
    /// let map = typemap!(u32 = 23u32, &str = "Hello, world!");
    ///
    /// println!("{}", map.get::<&str>());
    /// ```
    ///
    /// [`try_get`]: #tymethod.try_get
    fn get<T: 'static>(&self) -> &T
    where
        Self: Contains<T>,
    {
        self.try_get()
            .expect("Cannot get type! Check for errors at compile-time by using nightly.")
    }
}

// Terminating impl. It contains no items, and there are no items past it that you can get.
impl TypeMapGet for TyEnd {
    fn try_get<T: 'static>(&self) -> Option<&T> {
        None
    }
}

// # Recursive impl. We are either returning the current item or delegating to the inner type.

// on nightly, when Contains<T> is implemented, TypeMapGet::try_get should never return None
#[cfg(nightly)]
mod nightly_contains_impls {
    use super::{Contains, Ty, TypeMapGet};

    impl<A: 'static, R: TypeMapGet> Contains<A> for Ty<A, R> {}

    // TODO: use Borrow?
    impl<A: 'static, B: 'static, R: Contains<B>> Contains<B> for Ty<A, R> {}
}

// on stable, we can't properly constrain the trait, so we just do a blanket impl and let it panic at runtime.
#[cfg(not(nightly))]
impl<A: 'static, B: 'static, R: TypeMapGet> Contains<A> for Ty<B, R> {}

// TODO: use Borrow?
impl<V: 'static, R: TypeMapGet> TypeMapGet for Ty<V, R> {
    fn try_get<T: 'static>(&self) -> Option<&T> {
        Any::downcast_ref::<T>(&self.val).or_else(|| self.rest.try_get::<T>())
    }
}

// # End Recursive impl
// # Reference Impls. Thin shims that delegate to the Recursive Impl for references

impl<A: 'static, B: 'static, R: TypeMapGet> Contains<A> for &Ty<B, R> where Ty<B, R>: Contains<A> {}

impl<V: 'static, R: TypeMapGet> TypeMapGet for &Ty<V, R> {
    fn try_get<T: 'static>(&self) -> Option<&T> {
        Ty::<V, R>::try_get(*self)
    }
}

impl<A: 'static, B: 'static, R: TypeMapGet> Contains<A> for &mut Ty<B, R> where Ty<B, R>: Contains<A>
{}

impl<V: 'static, R: TypeMapGet> TypeMapGet for &mut Ty<V, R> {
    fn try_get<T: 'static>(&self) -> Option<&T> {
        Ty::<V, R>::try_get(*self)
    }
}

// # End Reference Impls

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_get() {
        type Test = typemap_ty!(u64, u32, u16, u8);
        let mut test: Test = typemap!(u64 = 4u64, u32 = 3u32, u16 = 2u16, u8 = 1u8);

        assert_eq!(test.get::<u8>(), &1u8);
        assert_eq!(test.get::<u16>(), &2u16);
        assert_eq!(test.get::<u32>(), &3u32);
        assert_eq!(test.get::<u64>(), &4u64);
        assert_eq!(test.try_get::<u128>(), None);

        let test_ref: typemap_ty!(u128, u16, ..&mut Test) =
            typemap!(u128 = 5u128, u16 = 16u16, ..&mut test);
        assert_eq!(test_ref.get::<u8>(), &1u8);
        assert_eq!(test_ref.get::<u16>(), &16u16);
        assert_eq!(test_ref.get::<u128>(), &5u128);

        assert_eq!(test.get::<u16>(), &2u16);

        let test_ref: typemap_ty!(u128, u16, ..&Test) =
            typemap!(u128 = 5u128, u16 = 16u16, ..&test);
        assert_eq!(test_ref.get::<u8>(), &1u8);
        assert_eq!(test_ref.get::<u16>(), &16u16);
        assert_eq!(test_ref.get::<u128>(), &5u128);

        assert_eq!(test.get::<u16>(), &2u16);

        let test: typemap_ty!(u128, u16, ..Test) = typemap!(u128 = 5u128, u16 = 16u16, ..test);
        assert_eq!(test.get::<u8>(), &1u8);
        // NOTE: making it so that you can access the original u16 gets a bit more difficult if you do this
        assert_eq!(test.get::<u16>(), &16u16);
        assert_eq!(test.get::<u128>(), &5u128);

        // TODO: we don't really have a *great* way of destructuring things at the moment.
        //  also not a publicly exposed way to do it.
        let Ty {
            val: u128_val,
            rest: Ty {
                val: u16_val,
                rest: test,
            },
        } = test;

        assert_eq!(u128_val, 5u128);
        assert_eq!(u16_val, 16u16);
        assert_eq!(test.get::<u16>(), &2u16)
    }
}