use std::alloc::Layout;
pub trait Niche: Sized {
type Output;
fn none() -> Self::Output;
fn is_none(value: &Self::Output) -> bool;
fn into_some(value: Self) -> Self::Output;
fn from_some(value: Self::Output) -> Self;
}
#[repr(transparent)]
pub struct ControlledOption<T>
where
T: Niche,
{
value: T::Output,
}
impl<T> ControlledOption<T>
where
T: Niche,
{
#[inline]
pub fn none() -> ControlledOption<T> {
let value = T::none();
debug_assert!(T::is_none(&value));
ControlledOption { value }
}
#[inline]
pub fn some(value: T) -> ControlledOption<T> {
let value = T::into_some(value);
debug_assert!(!T::is_none(&value));
ControlledOption { value }
}
#[inline]
pub fn is_none(&self) -> bool {
T::is_none(&self.value)
}
#[inline]
pub fn is_some(&self) -> bool {
!T::is_none(&self.value)
}
#[inline]
pub fn from_option(value: Option<T>) -> ControlledOption<T> {
value.into()
}
#[inline]
pub fn into_option(self) -> Option<T> {
self.into()
}
}
impl<T> Default for ControlledOption<T>
where
T: Niche,
{
#[inline]
fn default() -> ControlledOption<T> {
ControlledOption::none()
}
}
impl<T> From<T> for ControlledOption<T>
where
T: Niche,
{
#[inline]
fn from(value: T) -> ControlledOption<T> {
ControlledOption::some(value)
}
}
impl<T> From<Option<T>> for ControlledOption<T>
where
T: Niche,
{
#[inline]
fn from(value: Option<T>) -> ControlledOption<T> {
match value {
Some(value) => ControlledOption::some(value),
None => ControlledOption::none(),
}
}
}
impl<T> Into<Option<T>> for ControlledOption<T>
where
T: Niche,
{
#[inline]
fn into(self) -> Option<T> {
if T::is_none(&self.value) {
None
} else {
Some(T::from_some(self.value))
}
}
}
impl<T> Clone for ControlledOption<T>
where
T: Niche,
T::Output: Clone,
{
fn clone(&self) -> Self {
ControlledOption {
value: self.value.clone(),
}
}
}
impl<T> Copy for ControlledOption<T>
where
T: Niche,
T::Output: Copy,
{
}
impl<T> std::fmt::Debug for ControlledOption<T>
where
T: std::fmt::Debug + Niche,
T::Output: Clone,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.is_none() {
write!(f, "ControlledOption::None")
} else {
f.debug_tuple("ControlledOption::Some")
.field(&T::from_some(self.value.clone()))
.finish()
}
}
}
impl<T> PartialEq for ControlledOption<T>
where
T: Niche,
T::Output: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.value.eq(&other.value)
}
fn ne(&self, other: &Self) -> bool {
self.value.ne(&other.value)
}
}
impl<T> Eq for ControlledOption<T>
where
T: Niche,
T::Output: Eq,
{
}
impl<T> PartialOrd for ControlledOption<T>
where
T: Niche,
T::Output: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.value.partial_cmp(&other.value)
}
fn lt(&self, other: &Self) -> bool {
self.value.lt(&other.value)
}
fn le(&self, other: &Self) -> bool {
self.value.le(&other.value)
}
fn gt(&self, other: &Self) -> bool {
self.value.gt(&other.value)
}
fn ge(&self, other: &Self) -> bool {
self.value.ge(&other.value)
}
}
impl<T> Ord for ControlledOption<T>
where
T: Niche,
T::Output: Ord,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.value.cmp(&other.value)
}
fn max(self, other: Self) -> Self {
ControlledOption {
value: self.value.max(other.value),
}
}
fn min(self, other: Self) -> Self {
ControlledOption {
value: self.value.min(other.value),
}
}
fn clamp(self, min: Self, max: Self) -> Self {
ControlledOption {
value: self.value.clamp(min.value, max.value),
}
}
}
impl<T> std::hash::Hash for ControlledOption<T>
where
T: Niche,
T::Output: std::hash::Hash,
{
fn hash<H>(&self, state: &mut H)
where
H: std::hash::Hasher,
{
self.value.hash(state)
}
}
pub use controlled_option_macros::Niche;
#[doc(hidden)]
pub fn fill_struct_field_with_none<T>(field: *mut T)
where
T: Niche,
{
debug_assert!(Layout::new::<T>() == Layout::new::<T::Output>());
let repr = field as *mut T::Output;
unsafe { repr.write(T::none()) };
}
#[doc(hidden)]
pub fn struct_field_is_none<T>(field: *const T) -> bool
where
T: Niche,
{
debug_assert!(Layout::new::<T>() == Layout::new::<T::Output>());
let repr = field as *const T::Output;
T::is_none(unsafe { &*repr })
}
impl<'a, T> Niche for &'a T {
type Output = *const T;
#[inline]
fn none() -> Self::Output {
std::ptr::null()
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
value.is_null()
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { &*value }
}
}
impl<'a, T> Niche for &'a mut T {
type Output = *mut T;
#[inline]
fn none() -> Self::Output {
std::ptr::null_mut()
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
value.is_null()
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { &mut *value }
}
}
impl Niche for std::num::NonZeroI8 {
type Output = i8;
#[inline]
fn none() -> Self::Output {
0
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
*value == 0
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value.get()
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { Self::new_unchecked(value) }
}
}
impl Niche for std::num::NonZeroI16 {
type Output = i16;
#[inline]
fn none() -> Self::Output {
0
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
*value == 0
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value.get()
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { Self::new_unchecked(value) }
}
}
impl Niche for std::num::NonZeroI32 {
type Output = i32;
#[inline]
fn none() -> Self::Output {
0
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
*value == 0
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value.get()
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { Self::new_unchecked(value) }
}
}
impl Niche for std::num::NonZeroI64 {
type Output = i64;
#[inline]
fn none() -> Self::Output {
0
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
*value == 0
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value.get()
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { Self::new_unchecked(value) }
}
}
impl Niche for std::num::NonZeroIsize {
type Output = isize;
#[inline]
fn none() -> Self::Output {
0
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
*value == 0
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value.get()
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { Self::new_unchecked(value) }
}
}
impl Niche for std::num::NonZeroU8 {
type Output = u8;
#[inline]
fn none() -> Self::Output {
0
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
*value == 0
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value.get()
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { Self::new_unchecked(value) }
}
}
impl Niche for std::num::NonZeroU16 {
type Output = u16;
#[inline]
fn none() -> Self::Output {
0
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
*value == 0
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value.get()
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { Self::new_unchecked(value) }
}
}
impl Niche for std::num::NonZeroU32 {
type Output = u32;
#[inline]
fn none() -> Self::Output {
0
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
*value == 0
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value.get()
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { Self::new_unchecked(value) }
}
}
impl Niche for std::num::NonZeroU64 {
type Output = u64;
#[inline]
fn none() -> Self::Output {
0
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
*value == 0
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value.get()
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { Self::new_unchecked(value) }
}
}
impl Niche for std::num::NonZeroUsize {
type Output = usize;
#[inline]
fn none() -> Self::Output {
0
}
#[inline]
fn is_none(value: &Self::Output) -> bool {
*value == 0
}
#[inline]
fn into_some(value: Self) -> Self::Output {
value.get()
}
#[inline]
fn from_some(value: Self::Output) -> Self {
unsafe { Self::new_unchecked(value) }
}
}