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
use std::hash::Hash;

/// Type with a specific value we could reuse as `None` in `OptionInvalidValue`;
pub trait InvalidValue<T> {
    fn invalid_value() -> T;
}

/// Optimization for `Option<T>` where `T` implements an `InvalidValue` trait and reuses on of the
/// possible values of `T` to represents `None`. It's basically the same as `NonZero*`-types but more
/// powerful as it allows to reuse any value, not just `0` for any type, not just integers.
/// As a downside, it's not an `Option`, it's a separate type which is needed to be handled
/// explicitly.
///
/// ```
/// use obj_pool::invalid_value::{InvalidValue, OptionInvalidValue};
///
/// #[derive(PartialEq, Eq, Hash, Copy, Clone)]
/// struct A(u32);
///
/// impl InvalidValue<A> for A {
///   fn invalid_value() -> A {
///      A(155) // invalid value in our case
///   }
/// }
///
/// let mut v: OptionInvalidValue<A> = A(100).into();
/// assert!(v.is_some());
///
/// v = A(155).into();
/// assert!(v.is_none());
///
/// ```
#[derive(PartialEq, Debug, Copy, Clone, Eq, Hash)]
pub struct OptionInvalidValue<T: InvalidValue<T> + PartialEq + Copy + Eq + Hash>(T);

impl<T: InvalidValue<T> + PartialEq + Copy + Eq + Hash> OptionInvalidValue<T> {

    /// Make a `None` value
    pub fn none() -> OptionInvalidValue<T> {
        OptionInvalidValue(T::invalid_value())
    }

    /// Make an `Option<&T>`
    pub fn option(&self) -> Option<&T> {
        if self.is_some() {
            Some(&self.0)
        } else {
            None
        }
    }

    /// Get reference to underlying T, panics it it's `None`.
    pub fn as_ref_unchecked(&self) -> &T {
        if self.is_some() {
            &self.0
        } else {
            panic!("is none")
        }
    }

    /// Get mutable reference to underlying T, panics it it's `None`.
    pub fn as_ref_mut_unchecked(&mut self) -> &mut T {
        if self.is_some() {
            &mut self.0
        } else {
            panic!("is none")
        }
    }

    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
        Into::<Option<T>>::into(self).map(f)
    }
}

impl<T: InvalidValue<T> + PartialEq + Copy + Eq + Hash> IntoIterator for OptionInvalidValue<T> {
    type Item = T;
    type IntoIter = ::std::option::IntoIter<T>;

    fn into_iter(self) -> Self::IntoIter {
        Into::<Option<T>>::into(self).into_iter()
    }
}

impl<T: InvalidValue<T> + PartialEq + Copy + Eq + Hash> From<Option<T>> for OptionInvalidValue<T> {
    fn from(v: Option<T>) -> Self {
        match v {
            Some(v) => OptionInvalidValue(v),
            _ => OptionInvalidValue::none(),
        }
    }
}

impl<T: InvalidValue<T> + PartialEq + Copy + Eq + Hash> Into<Option<T>> for OptionInvalidValue<T> {
    fn into(self) -> Option<T> {
        let val = self.0;
        if val == T::invalid_value() {
            None
        } else {
            Some(val)
        }
    }
}

/// Wraps a value in `OptionInvalidValue`, treats `T::invalid_value` as `None`
impl<T: InvalidValue<T> + PartialEq + Copy + Eq + Hash> From<T> for OptionInvalidValue<T> {
    fn from(v: T) -> Self {
        OptionInvalidValue(v)
    }
}

impl<T: InvalidValue<T> + PartialEq + Copy + Eq + Hash> OptionInvalidValue<T> {
    /// Checks if `OptionInvalidValue` is `Some`
    pub fn is_some(&self) -> bool {
        self.0 != T::invalid_value()
    }

    /// Checks if `OptionInvalidValue` is `None`
    pub fn is_none(&self) -> bool {
        !self.is_some()
    }

    /// Unwraps the `OptionInvalidValue`, panics in case of `None`.
    pub fn unwrap(self) -> T {
        if self.is_none() {
            panic!("unwrap on None");
        }
        self.0
    }
}