fera_optional/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5#![doc(html_root_url = "https://docs.rs/fera-optional/0.2.0/")]
6
7//! An optional value trait and some implementations.
8//!
9//! There are two main uses for this:
10//!
11//! 1. To abstract over the representation of optional values in generic code
12//!    ([`Optional`] trait);
13//! 2. To represent optional value in a space efficient way ([`OptionalBool`], [`OptionalMax`],
14//!    [`OptionalMin`], etc).
15//!
16//! Note that this is complementary to [`std::option::Option`], not a replacement. The idea is to
17//! use [`std::option::Option`] interface by converting an [`Optional`] value to an
18//! [`std::option::Option`] value.
19//!
20//! The [`optional`][optional_crate] crate is similar, but we think that this module is more
21//! generic, mainly because `optional` crate is not concerned with the use case 1.
22//!
23//! This crate can be used through [`fera`] crate.
24//!
25//! # Example
26//!
27//! One can use `OptionalMax<usize>` to represent an optional `usize` where the `None` value is
28//! represented by `usize::MAX`.
29//!
30//! ```
31//! use fera_optional::*;
32//! use std::mem;
33//!
34//! assert_eq!(mem::size_of::<usize>(), mem::size_of::<OptionalMax<usize>>());
35//!
36//! // default
37//! let y = OptionalMax::<usize>::default();
38//! assert_eq!(None, y.into_option());
39//!
40//! // from a value
41//! let x = OptionalMax::from(10usize);
42//! assert_eq!(Some(&10), x.to_option_ref());
43//! assert_eq!(10, *x.to_option_ref().unwrap());
44//!
45//! // from an optional value
46//! let z = Some(10);
47//! let w = OptionalMax::from(z);
48//! assert_eq!(Some(10), w.into_option());
49//! ```
50//!
51//! Using `OptionalBool`
52//!
53//! ```
54//! use fera_optional::*;
55//! use std::mem;
56//!
57//! assert_eq!(1, mem::size_of::<OptionalBool>());
58//!
59//! let mut x = OptionalBool::from(false);
60//! assert!(!*x.to_option_ref().unwrap());
61//!
62//! // change the value
63//! *x.to_option_mut().unwrap() = true;
64//! assert!(*x.to_option_ref().unwrap());
65//! ```
66//!
67//! [`fera`]: https://docs.rs/fera
68//! [`OptionalBool`]: struct.OptionalBool.html
69//! [optional_crate]: https://crates.io/crates/optional
70//! [`OptionalMax`]: type.OptionalMax.html
71//! [`OptionalMin`]: type.OptionalMin.html
72//! [`Optional`]: trait.Optional.html
73//! [`std::option::Option`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html
74
75extern crate num_traits;
76
77use std::fmt;
78use std::marker::PhantomData;
79use std::mem;
80
81use num_traits::bounds::Bounded;
82
83/// An `Optional` that uses `T::max_value()` as `None`.
84pub type OptionalMax<T> = Optioned<T, MaxNone<T>>;
85
86/// An `Optional` that uses `T::min_value()` as `None`.
87pub type OptionalMin<T> = Optioned<T, MinNone<T>>;
88
89/// A trait that represents an optional value.
90///
91/// This is a complement to [`std::option::Option`] that allows implementations to choose how to
92/// represent `Some` and `None`.
93///
94/// [`std::option::Option`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html
95pub trait Optional<T>: Default + From<T> {
96    /// Returns an `Option<&T>` that is equivalent to this `Optional`.
97    fn to_option_ref(&self) -> Option<&T>;
98
99    /// Returns an `Option<&mut T>` that is equivalent to this `Optional`.
100    fn to_option_mut(&mut self) -> Option<&mut T>;
101
102    /// Converts this `Optional` to the equivalent `Option`.
103    fn into_option(self) -> Option<T>;
104}
105
106impl<T> Optional<T> for Option<T> {
107    fn to_option_ref(&self) -> Option<&T> {
108        self.as_ref()
109    }
110
111    fn to_option_mut(&mut self) -> Option<&mut T> {
112        self.as_mut()
113    }
114
115    fn into_option(self) -> Option<T> {
116        self
117    }
118}
119
120/// An `Optional` that represents `None` with a specified value (`B::none()`) of `T` domain.
121#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
122pub struct Optioned<T, B: BuildNone<T>> {
123    value: T,
124    _marker: PhantomData<B>,
125}
126
127/// A builder for `None` values for type `T`.
128pub trait BuildNone<T> {
129    fn none() -> T;
130
131    fn desc() -> &'static str;
132}
133
134impl<T: Eq + fmt::Debug, B: BuildNone<T>> fmt::Debug for Optioned<T, B> {
135    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
136        write!(fmt, "Optional{}::{:?}", B::desc(), self.to_option_ref())
137    }
138}
139
140impl<T, B: BuildNone<T>> Default for Optioned<T, B> {
141    fn default() -> Self {
142        Optioned {
143            value: B::none(),
144            _marker: PhantomData,
145        }
146    }
147}
148
149impl<T, B: BuildNone<T>> From<Option<T>> for Optioned<T, B> {
150    fn from(value: Option<T>) -> Self {
151        if let Some(v) = value {
152            Optioned::from(v)
153        } else {
154            Optioned::default()
155        }
156    }
157}
158
159impl<T, B: BuildNone<T>> From<T> for Optioned<T, B> {
160    fn from(value: T) -> Self {
161        Optioned {
162            value: value,
163            _marker: PhantomData,
164        }
165    }
166}
167
168impl<T: Eq, B: BuildNone<T>> Optional<T> for Optioned<T, B> {
169    #[inline(always)]
170    fn to_option_ref(&self) -> Option<&T> {
171        if self.value == B::none() {
172            None
173        } else {
174            Some(&self.value)
175        }
176    }
177
178    #[inline(always)]
179    fn to_option_mut(&mut self) -> Option<&mut T> {
180        if self.value == B::none() {
181            None
182        } else {
183            Some(&mut self.value)
184        }
185    }
186
187    #[inline(always)]
188    fn into_option(self) -> Option<T> {
189        if self.value == B::none() {
190            None
191        } else {
192            Some(self.value)
193        }
194    }
195}
196
197/// Creates `T::max_value()` as `None`.
198#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
199pub struct MaxNone<T>(PhantomData<T>);
200
201impl<T: Bounded> BuildNone<T> for MaxNone<T> {
202    fn none() -> T {
203        T::max_value()
204    }
205
206    fn desc() -> &'static str {
207        "Max"
208    }
209}
210
211/// Creates `T::min_value()` as `None`.
212#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
213pub struct MinNone<T>(PhantomData<T>);
214
215impl<T: Bounded> BuildNone<T> for MinNone<T> {
216    fn none() -> T {
217        T::min_value()
218    }
219
220    fn desc() -> &'static str {
221        "Min"
222    }
223}
224
225/// An `Optional` for `bool` with 1 byte size.
226/// `std::option::Option<bool>` have size 1 since rustc 1.23.
227#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
228pub struct OptionalBool(OptionalMax<u8>);
229
230impl From<bool> for OptionalBool {
231    fn from(value: bool) -> Self {
232        OptionalBool(OptionalMax::from(unsafe {
233            mem::transmute::<bool, u8>(value)
234        }))
235    }
236}
237
238impl fmt::Debug for OptionalBool {
239    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
240        write!(fmt, "OptionalBool::{:?}", self.to_option_ref())
241    }
242}
243
244impl From<Option<bool>> for OptionalBool {
245    fn from(value: Option<bool>) -> Self {
246        if let Some(v) = value {
247            OptionalBool::from(v)
248        } else {
249            OptionalBool::default()
250        }
251    }
252}
253
254impl Optional<bool> for OptionalBool {
255    #[inline(always)]
256    fn to_option_ref(&self) -> Option<&bool> {
257        unsafe { mem::transmute(self.0.to_option_ref()) }
258    }
259
260    #[inline(always)]
261    fn to_option_mut(&mut self) -> Option<&mut bool> {
262        unsafe { mem::transmute(self.0.to_option_mut()) }
263    }
264
265    #[inline(always)]
266    fn into_option(self) -> Option<bool> {
267        self.0.into_option().map(|v| v == 1)
268    }
269}
270
271#[cfg(test)]
272pub trait OptionalTest {
273    type Item: ::std::fmt::Debug + PartialEq;
274    type Sut: Optional<Self::Item>;
275
276    fn values() -> (Self::Item, Self::Item);
277
278    fn sut_default() -> Self::Sut {
279        Self::Sut::default()
280    }
281
282    fn sut_from(value: Self::Item) -> Self::Sut {
283        Self::Sut::from(value)
284    }
285
286    fn to_option_ref() {
287        let (v1, v2) = Self::values();
288        let (x1, x2) = Self::values();
289        assert_eq!(None, Self::sut_default().to_option_ref());
290        assert_eq!(Some(&v1), Self::sut_from(x1).to_option_ref());
291        assert_eq!(Some(&v2), Self::sut_from(x2).to_option_ref());
292    }
293
294    fn to_option_mut() {
295        let (mut v1, mut v2) = Self::values();
296        let (x1, x2) = Self::values();
297        assert_eq!(None, Self::sut_default().to_option_mut());
298        assert_eq!(Some(&mut v1), Self::sut_from(x1).to_option_mut());
299        assert_eq!(Some(&mut v2), Self::sut_from(x2).to_option_mut());
300
301        let (x1, x2) = Self::values();
302        let mut o = Self::sut_from(x1);
303        *o.to_option_mut().unwrap() = x2;
304        assert_eq!(Some(&v2), o.to_option_ref());
305        assert_eq!(Some(&mut v2), o.to_option_mut());
306        assert_eq!(Some(v2), o.into_option());
307    }
308
309    fn into_option() {
310        let (v1, v2) = Self::values();
311        let (x1, x2) = Self::values();
312        assert_eq!(None, Self::sut_default().into_option());
313        assert_eq!(Some(v1), Self::sut_from(x1).into_option());
314        assert_eq!(Some(v2), Self::sut_from(x2).into_option());
315    }
316}
317
318#[cfg(test)]
319mod tests {
320    macro_rules! delegate_tests {
321        ($T: ident, $($names: ident),+) => (
322            $(
323                #[test]
324                fn $names() {
325                    $T::$names();
326                }
327            )*
328        )
329    }
330
331    mod optional_bool {
332        use *;
333        struct T;
334
335        impl OptionalTest for T {
336            type Item = bool;
337            type Sut = OptionalBool;
338
339            fn values() -> (Self::Item, Self::Item) {
340                (false, true)
341            }
342        }
343
344        delegate_tests!{T, to_option_ref, to_option_mut, into_option}
345
346        #[test]
347        fn debug() {
348            assert_eq!(
349                "OptionalBool::None",
350                format!("{:?}", OptionalBool::default())
351            );
352            assert_eq!(
353                "OptionalBool::Some(true)",
354                format!("{:?}", OptionalBool::from(true))
355            );
356            assert_eq!(
357                "OptionalBool::Some(false)",
358                format!("{:?}", OptionalBool::from(false))
359            );
360        }
361    }
362
363    mod optioned_u32 {
364        use *;
365        struct T;
366
367        impl OptionalTest for T {
368            type Item = u32;
369            type Sut = OptionalMax<u32>;
370
371            fn values() -> (Self::Item, Self::Item) {
372                (10, 50)
373            }
374        }
375
376        delegate_tests!{T, to_option_ref, to_option_mut, into_option}
377
378        #[test]
379        fn debug() {
380            assert_eq!(
381                "OptionalMax::None",
382                format!("{:?}", OptionalMax::<u32>::default())
383            );
384            assert_eq!(
385                "OptionalMax::Some(10)",
386                format!("{:?}", OptionalMax::from(10u32))
387            );
388
389            assert_eq!(
390                "OptionalMin::None",
391                format!("{:?}", OptionalMin::<u32>::default())
392            );
393            assert_eq!(
394                "OptionalMin::Some(10)",
395                format!("{:?}", OptionalMin::from(10u32))
396            );
397        }
398    }
399}