#![doc(html_root_url = "https://docs.rs/fera-optional/0.2.0/")]
extern crate num_traits;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use num_traits::bounds::Bounded;
pub type OptionalMax<T> = Optioned<T, MaxNone<T>>;
pub type OptionalMin<T> = Optioned<T, MinNone<T>>;
pub trait Optional<T>: Default + From<T> {
fn to_option_ref(&self) -> Option<&T>;
fn to_option_mut(&mut self) -> Option<&mut T>;
fn into_option(self) -> Option<T>;
}
impl<T> Optional<T> for Option<T> {
fn to_option_ref(&self) -> Option<&T> {
self.as_ref()
}
fn to_option_mut(&mut self) -> Option<&mut T> {
self.as_mut()
}
fn into_option(self) -> Option<T> {
self
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Optioned<T, B: BuildNone<T>> {
value: T,
_marker: PhantomData<B>,
}
pub trait BuildNone<T> {
fn none() -> T;
fn desc() -> &'static str;
}
impl<T: Eq + fmt::Debug, B: BuildNone<T>> fmt::Debug for Optioned<T, B> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Optional{}::{:?}", B::desc(), self.to_option_ref())
}
}
impl<T, B: BuildNone<T>> Default for Optioned<T, B> {
fn default() -> Self {
Optioned {
value: B::none(),
_marker: PhantomData,
}
}
}
impl<T, B: BuildNone<T>> From<Option<T>> for Optioned<T, B> {
fn from(value: Option<T>) -> Self {
if let Some(v) = value {
Optioned::from(v)
} else {
Optioned::default()
}
}
}
impl<T, B: BuildNone<T>> From<T> for Optioned<T, B> {
fn from(value: T) -> Self {
Optioned {
value: value,
_marker: PhantomData,
}
}
}
impl<T: Eq, B: BuildNone<T>> Optional<T> for Optioned<T, B> {
#[inline(always)]
fn to_option_ref(&self) -> Option<&T> {
if self.value == B::none() {
None
} else {
Some(&self.value)
}
}
#[inline(always)]
fn to_option_mut(&mut self) -> Option<&mut T> {
if self.value == B::none() {
None
} else {
Some(&mut self.value)
}
}
#[inline(always)]
fn into_option(self) -> Option<T> {
if self.value == B::none() {
None
} else {
Some(self.value)
}
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub struct MaxNone<T>(PhantomData<T>);
impl<T: Bounded> BuildNone<T> for MaxNone<T> {
fn none() -> T {
T::max_value()
}
fn desc() -> &'static str {
"Max"
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub struct MinNone<T>(PhantomData<T>);
impl<T: Bounded> BuildNone<T> for MinNone<T> {
fn none() -> T {
T::min_value()
}
fn desc() -> &'static str {
"Min"
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
pub struct OptionalBool(OptionalMax<u8>);
impl From<bool> for OptionalBool {
fn from(value: bool) -> Self {
OptionalBool(OptionalMax::from(unsafe {
mem::transmute::<bool, u8>(value)
}))
}
}
impl fmt::Debug for OptionalBool {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "OptionalBool::{:?}", self.to_option_ref())
}
}
impl From<Option<bool>> for OptionalBool {
fn from(value: Option<bool>) -> Self {
if let Some(v) = value {
OptionalBool::from(v)
} else {
OptionalBool::default()
}
}
}
impl Optional<bool> for OptionalBool {
#[inline(always)]
fn to_option_ref(&self) -> Option<&bool> {
unsafe { mem::transmute(self.0.to_option_ref()) }
}
#[inline(always)]
fn to_option_mut(&mut self) -> Option<&mut bool> {
unsafe { mem::transmute(self.0.to_option_mut()) }
}
#[inline(always)]
fn into_option(self) -> Option<bool> {
self.0.into_option().map(|v| v == 1)
}
}
#[cfg(test)]
pub trait OptionalTest {
type Item: ::std::fmt::Debug + PartialEq;
type Sut: Optional<Self::Item>;
fn values() -> (Self::Item, Self::Item);
fn sut_default() -> Self::Sut {
Self::Sut::default()
}
fn sut_from(value: Self::Item) -> Self::Sut {
Self::Sut::from(value)
}
fn to_option_ref() {
let (v1, v2) = Self::values();
let (x1, x2) = Self::values();
assert_eq!(None, Self::sut_default().to_option_ref());
assert_eq!(Some(&v1), Self::sut_from(x1).to_option_ref());
assert_eq!(Some(&v2), Self::sut_from(x2).to_option_ref());
}
fn to_option_mut() {
let (mut v1, mut v2) = Self::values();
let (x1, x2) = Self::values();
assert_eq!(None, Self::sut_default().to_option_mut());
assert_eq!(Some(&mut v1), Self::sut_from(x1).to_option_mut());
assert_eq!(Some(&mut v2), Self::sut_from(x2).to_option_mut());
let (x1, x2) = Self::values();
let mut o = Self::sut_from(x1);
*o.to_option_mut().unwrap() = x2;
assert_eq!(Some(&v2), o.to_option_ref());
assert_eq!(Some(&mut v2), o.to_option_mut());
assert_eq!(Some(v2), o.into_option());
}
fn into_option() {
let (v1, v2) = Self::values();
let (x1, x2) = Self::values();
assert_eq!(None, Self::sut_default().into_option());
assert_eq!(Some(v1), Self::sut_from(x1).into_option());
assert_eq!(Some(v2), Self::sut_from(x2).into_option());
}
}
#[cfg(test)]
mod tests {
macro_rules! delegate_tests {
($T: ident, $($names: ident),+) => (
$(
#[test]
fn $names() {
$T::$names();
}
)*
)
}
mod optional_bool {
use *;
struct T;
impl OptionalTest for T {
type Item = bool;
type Sut = OptionalBool;
fn values() -> (Self::Item, Self::Item) {
(false, true)
}
}
delegate_tests!{T, to_option_ref, to_option_mut, into_option}
#[test]
fn debug() {
assert_eq!(
"OptionalBool::None",
format!("{:?}", OptionalBool::default())
);
assert_eq!(
"OptionalBool::Some(true)",
format!("{:?}", OptionalBool::from(true))
);
assert_eq!(
"OptionalBool::Some(false)",
format!("{:?}", OptionalBool::from(false))
);
}
}
mod optioned_u32 {
use *;
struct T;
impl OptionalTest for T {
type Item = u32;
type Sut = OptionalMax<u32>;
fn values() -> (Self::Item, Self::Item) {
(10, 50)
}
}
delegate_tests!{T, to_option_ref, to_option_mut, into_option}
#[test]
fn debug() {
assert_eq!(
"OptionalMax::None",
format!("{:?}", OptionalMax::<u32>::default())
);
assert_eq!(
"OptionalMax::Some(10)",
format!("{:?}", OptionalMax::from(10u32))
);
assert_eq!(
"OptionalMin::None",
format!("{:?}", OptionalMin::<u32>::default())
);
assert_eq!(
"OptionalMin::Some(10)",
format!("{:?}", OptionalMin::from(10u32))
);
}
}
}