use crate::{
Cast, ConstInit, InvalidValue, NicheValueError, NonValueI8, NonValueI16, NonValueI32,
NonValueI64, NonValueI128, NonValueIsize, NonValueU8, NonValueU16, NonValueU32, NonValueU64,
NonValueU128, NonValueUsize, NonZero, Overflow, unwrap,
};
#[doc = crate::_tags!(maybe niche)]
#[doc = crate::_doc_location!("num/grain/niche")]
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MaybeNiche<T: Copy>(pub T);
macro_rules! impl_maybe {
() => {
impl_maybe!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
};
($($T:ty),+) => {
$( impl_maybe![% false, $T, $T]; )+
$( impl_maybe![% false, $T, NonNiche<$T>, *get, ^new]; )+
$( impl_maybe![% true, $T, NonZero<$T>, *get, ^new, @non0]; )+
$crate::paste!{ $(
impl_maybe![% true, $T, [<NonValue $T:camel>] <V>, <const V: $T>,
*get, ^new];
)+ }
};
(%
$is_niche:literal, // IS_NICHE
$prim:ty,
$T:ty $(, <const $V:ident : $v:ty>)?
$(, *$get:ident)? // for: get_prim, prim
$(, ^$new:ident)? // for: from_prim, *_unchecked
$(, @$non0:ident)? // identifies nonzero types, for: from_prim_lossy
) => {
impl $(<const $V: $v>)? ConstInit for MaybeNiche<$T> where $T: ConstInit {
const INIT: Self = Self::new(<$T>::INIT);
}
impl $(<const $V: $v>)? MaybeNiche<$T> {
pub const IS_NICHE: bool = $is_niche;
pub const IS_CONTIGUOUS: bool = {
#[crate::compile(none($(<const $V: $v>)?))]
const fn _is_contiguous $(<const $V: $v>)? () -> bool { true }
#[crate::compile(some($(<const $V: $v>)?))]
const fn _is_contiguous $(<const $V: $v>)? () -> bool {
V == <$prim>::MIN || V == <$prim>::MAX
}
_is_contiguous$(::<$V>)?()
};
#[allow(unused_comparisons, reason = "for unsigned types")]
pub const HAS_NEGATIVE: bool = Self::MIN.prim() < 0;
pub const MIN: Self = Self(<$T>::MIN);
pub const MAX: Self = Self(<$T>::MAX);
pub const ZERO: Option<Self> = unwrap![ok_some Self::try_from_prim(0)];
#[must_use] #[inline(always)]
pub const fn new(value: $T) -> Self { Self(value) }
#[inline(always)]
pub const fn try_from_prim(primitive: $prim) -> Result<Self, InvalidValue> {
#[crate::compile(some($($new)?))]
const fn _new $(<const $V: $v>)? (v: $prim) -> Option<$T> { $( <$T>::$new(v) )? }
#[crate::compile(none($($new)?))]
const fn _new $(<const $V: $v>)? (v: $prim) -> Option<$T> { Some(v) }
Ok(Self(unwrap![some_ok_or? _new(primitive), crate::InvalidValue]))
}
#[must_use] #[inline(always)]
#[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))]
#[cfg_attr(nightly_doc, doc(cfg(feature = "unsafe_niche")))]
pub const unsafe fn from_prim_unchecked(primitive: $prim) -> Self {
#[crate::compile(some($($new)?))]
const fn _new $(<const $V: $v>)? (v: $prim) -> $T { $crate::paste! {
unsafe { $( <$T>:: [< $new _unchecked >](v) )? }
}}
#[crate::compile(none($($new)?))]
const fn _new $(<const $V: $v>)? (v: $prim) -> $T { v }
Self(_new(primitive))
}
#[must_use] #[inline(always)]
pub const fn from_prim_lossy(value: $prim) -> Self {
#[crate::compile(all(some($($new)?), some($($non0)?)))]
const fn _lossy $(<const $V: $v>)? (v: $prim) -> $T {
if v == 0 { <$T>::MIN }
else {
#[cfg(any(feature = "safe_num", not(feature = "unsafe_niche")))] { unwrap![some <$T>::new(v)] }
#[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))] { unwrap![some_guaranteed_or_ub <$T>::new(v)] }
}
}
#[crate::compile(all(some($($new)?), none($($non0)?)))]
const fn _lossy $(<const $V: $v>)? (v: $prim) -> $T { $crate::paste! {
$( <$T>:: [< $new _lossy >](v) )?
}}
#[crate::compile(none($($new)?))]
const fn _lossy $(<const $V: $v>)? (v: $prim) -> $T { v }
Self(_lossy(value))
}
#[inline(always)]
pub const fn try_from_usize(value: usize) -> Result<Self, NicheValueError> {
#[crate::compile(some($($new)?))]
const fn _new $(<const $V: $v>)? (v: $prim) -> Option<$T> { $( <$T>::$new(v) )? }
#[crate::compile(none($($new)?))]
const fn _new $(<const $V: $v>)? (v: $prim) -> Option<$T> { Some(v) }
let prim = $crate::paste! { Cast(value).[<checked_cast_to_ $prim>]() };
let prim = unwrap![ok_err_map? prim, |e| NicheValueError::from_overflow(e)];
Ok(Self(unwrap![some_ok_or? _new(prim), NicheValueError::InvalidValue]))
}
#[must_use] #[inline(always)]
pub const fn from_usize_saturating(value: usize) -> Self {
let prim = $crate::paste! { Cast(value).[<saturating_cast_to_ $prim>]() };
Self::from_prim_lossy(prim)
}
#[must_use] #[inline(always)]
pub const fn from_usize_wrapping(value: usize) -> Self {
let prim = $crate::paste! { Cast(value).[<wrapping_cast_to_ $prim>]() };
Self::from_prim_lossy(prim)
}
#[must_use] #[inline(always)]
pub const fn is_niche(self) -> bool { Self::IS_NICHE }
#[must_use] #[inline(always)]
pub const fn is_contiguous(self) -> bool { Self::IS_CONTIGUOUS }
#[must_use] #[inline(always)]
pub const fn has_negative(self) -> bool { Self::HAS_NEGATIVE }
#[must_use] #[inline(always)]
pub const fn has_zero(self) -> bool { Self::ZERO.is_some() }
#[must_use] #[inline(always)]
pub const fn eq(self, other: Self) -> bool { self.prim() == other.prim() }
#[must_use] #[inline(always)]
pub const fn get(self) -> $T { self.0 }
#[must_use] #[inline(always)]
pub const fn repr(self) -> $T { self.get() }
#[must_use] #[inline(always)]
pub const fn get_prim(self) -> $prim { self.0 $( . $get() )? }
#[must_use] #[inline(always)]
pub const fn prim(self) -> $prim { self.get_prim() }
#[inline(always)]
pub const fn try_to_usize(self) -> Result<usize, Overflow> {
Cast(self.get_prim()).checked_cast_to_usize()
}
#[must_use] #[inline(always)]
pub const fn to_usize_saturating(self) -> usize {
Cast(self.get_prim()).saturating_cast_to_usize()
}
#[must_use] #[inline(always)]
pub const fn to_usize_wrapping(self) -> usize {
Cast(self.get_prim()).wrapping_cast_to_usize()
}
}
};
}
impl_maybe![];
#[doc = crate::_tags!(no niche)]
#[doc = crate::_doc_location!("num/grain/niche")]
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NonNiche<T: Copy>(pub T);
#[rustfmt::skip]
impl<T: Copy> NonNiche<T> {
#[must_use] #[inline(always)]
pub const fn new(value: T) -> Option<Self> { Some(Self(value)) }
#[must_use] #[inline(always)]
#[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))]
#[cfg_attr(nightly_doc, doc(cfg(feature = "unsafe_niche")))]
pub const unsafe fn new_unchecked(value: T) -> Self { Self(value) }
#[must_use] #[inline(always)]
pub const fn new_lossy(value: T) -> Self { Self(value) }
#[must_use] #[inline(always)]
pub const fn get(self) -> T { self.0 }
}
#[rustfmt::skip]
impl<T: Copy> From<T> for NonNiche<T> {
#[inline(always)]
fn from(value: T) -> Self { Self(value) }
}
impl<T: Copy + ConstInit> ConstInit for NonNiche<T> {
const INIT: Self = Self(T::INIT);
}
macro_rules! impl_non {
() => { impl_non!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); };
($($prim:ty),+) => {
$(
impl NonNiche<$prim> {
pub const MIN: Self = Self(<$prim>::MIN);
pub const MAX: Self = Self(<$prim>::MAX);
}
)+
};
}
impl_non![];
#[cfg(test)]
mod tests {
use super::{MaybeNiche, NonNiche, NonValueU8, NonZero};
#[test]
fn maybe_niche() {
let u = MaybeNiche(3_u8);
let nn = MaybeNiche(NonNiche::<u8>::new(3).unwrap());
let n0 = MaybeNiche(NonZero::<u8>::new(3).unwrap());
let nv0 = MaybeNiche(NonValueU8::<0>::new(3).unwrap());
let nv1 = MaybeNiche(NonValueU8::<1>::new(3).unwrap());
assert_eq![u.is_contiguous(), true];
assert_eq![u.is_niche(), false];
assert_eq![u.has_zero(), true];
assert_eq![nn.is_contiguous(), true];
assert_eq![nn.is_niche(), false];
assert_eq![nn.has_zero(), true];
assert_eq![n0.is_contiguous(), true];
assert_eq![n0.is_niche(), true];
assert_eq![n0.has_zero(), false];
assert_eq![nv0.is_contiguous(), true];
assert_eq![nv0.is_niche(), true];
assert_eq![nv0.has_zero(), false];
assert_eq![nv1.is_contiguous(), false];
assert_eq![nv1.is_niche(), true];
assert_eq![nv1.has_zero(), true];
}
}