use core::fmt::Debug;
use core::hash::Hash;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
use core::ptr::addr_of_mut;
use super::init;
#[derive(Debug)]
pub struct Maybe<T, G = ()> {
some: bool,
value: MaybeUninit<T>,
_tag: PhantomData<G>,
}
impl<T, G> Maybe<T, G> {
pub fn new(value: Option<T>) -> Self {
match value {
Some(v) => Self::some(v),
None => Self::none(),
}
}
pub const fn none() -> Self {
Self {
some: false,
value: MaybeUninit::uninit(),
_tag: PhantomData,
}
}
pub const fn some(value: T) -> Self {
Self {
some: true,
value: MaybeUninit::new(value),
_tag: PhantomData,
}
}
pub fn init_none() -> impl init::Init<Self> {
unsafe {
init::init_from_closure(move |slot: *mut Self| {
addr_of_mut!((*slot).some).write(false);
Ok(())
})
}
}
pub fn init_some<I: init::Init<T, E>, E>(value: I) -> impl init::Init<Self, E> {
Self::init(Some(value))
}
pub fn init<I: init::Init<T, E>, E>(value: Option<I>) -> impl init::Init<Self, E> {
unsafe {
init::init_from_closure(move |slot: *mut Self| {
let some = value.is_some();
if let Some(value) = value {
value.__init(addr_of_mut!((*slot).value) as _)?;
}
addr_of_mut!((*slot).some).write(some);
Ok(())
})
}
}
pub fn clear(&mut self) {
if self.some {
unsafe {
let slot = addr_of_mut!(*self);
addr_of_mut!((*slot).some).write(false);
let value = addr_of_mut!((*slot).value) as *mut T;
core::ptr::drop_in_place(value);
}
}
}
pub fn reinit<I: init::Init<Self>>(&mut self, value: I) {
unwrap!(Self::try_reinit(self, value));
}
pub fn try_reinit<I: init::Init<Self, E>, E>(&mut self, value: I) -> Result<(), E> {
self.clear();
unsafe {
let slot = addr_of_mut!(*self);
value.__init(slot)
}
}
pub fn as_mut(&mut self) -> Maybe<&mut T, G> {
if self.some {
Maybe::some(unsafe { self.value.assume_init_mut() })
} else {
Maybe::none()
}
}
pub fn as_ref(&self) -> Maybe<&T, G> {
if self.some {
Maybe::some(unsafe { self.value.assume_init_ref() })
} else {
Maybe::none()
}
}
pub fn as_opt_mut(&mut self) -> Option<&mut T> {
if self.some {
Some(unsafe { self.value.assume_init_mut() })
} else {
None
}
}
pub fn as_opt_ref(&self) -> Option<&T> {
if self.some {
Some(unsafe { self.value.assume_init_ref() })
} else {
None
}
}
pub fn as_deref(&self) -> Maybe<&T::Target, G>
where
T: Deref,
{
match self.as_opt_ref() {
Some(t) => Maybe::some(t.deref()),
None => Maybe::none(),
}
}
pub fn as_deref_mut(&mut self) -> Maybe<&mut T::Target, G>
where
T: DerefMut,
{
match self.as_opt_mut() {
Some(t) => Maybe::some(t.deref_mut()),
None => Maybe::none(),
}
}
pub fn as_opt_deref(&self) -> Option<&T::Target>
where
T: Deref,
{
match self.as_opt_ref() {
Some(t) => Some(t.deref()),
None => None,
}
}
pub fn as_opt_deref_mut(&mut self) -> Option<&mut T::Target>
where
T: DerefMut,
{
match self.as_opt_mut() {
Some(t) => Some(t.deref_mut()),
None => None,
}
}
pub fn into_option(mut self) -> Option<T> {
if !self.some {
return None;
}
Some(unsafe {
let slot = addr_of_mut!(self);
let ret = core::ptr::read(addr_of_mut!((*slot).value) as *mut _);
self.some = false;
ret
})
}
pub fn is_none(&self) -> bool {
!self.some
}
pub fn is_some(&self) -> bool {
self.some
}
}
impl<T, G> Drop for Maybe<T, G> {
fn drop(&mut self) {
self.clear();
}
}
impl<T, G> Default for Maybe<T, G> {
fn default() -> Self {
Self::none()
}
}
impl<T, G> From<Option<T>> for Maybe<T, G> {
fn from(value: Option<T>) -> Self {
Self::new(value)
}
}
impl<T, G> From<Maybe<T, G>> for Option<T> {
fn from(value: Maybe<T, G>) -> Self {
value.into_option()
}
}
impl<T, G> Clone for Maybe<T, G>
where
T: Clone,
{
fn clone(&self) -> Self {
Maybe::<_, G>::new(self.as_opt_ref().cloned())
}
}
impl<T, G> PartialEq for Maybe<T, G>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.as_opt_ref() == other.as_opt_ref()
}
}
impl<T, G> Eq for Maybe<T, G> where T: Eq {}
impl<T, G> Hash for Maybe<T, G>
where
T: Hash,
{
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.as_opt_ref().hash(state)
}
}
#[cfg(feature = "defmt")]
impl<T, G> defmt::Format for Maybe<T, G>
where
T: defmt::Format,
{
fn format(&self, f: defmt::Formatter<'_>) {
if self.is_none() {
defmt::write!(f, "None")
} else {
defmt::write!(f, "Some({})", unsafe { self.value.assume_init_ref() })
}
}
}
#[cfg(test)]
mod tests {
use super::Maybe;
macro_rules! droppable {
() => {
static COUNT: core::sync::atomic::AtomicI32 = core::sync::atomic::AtomicI32::new(0);
#[derive(Eq, Ord, PartialEq, PartialOrd)]
struct Droppable(());
impl Droppable {
fn new() -> Self {
COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
Droppable(())
}
fn count() -> i32 {
COUNT.load(core::sync::atomic::Ordering::Relaxed)
}
}
impl Drop for Droppable {
fn drop(&mut self) {
COUNT.fetch_sub(1, core::sync::atomic::Ordering::Relaxed);
}
}
impl Clone for Droppable {
fn clone(&self) -> Self {
COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
Self(())
}
}
};
}
#[test]
fn drop() {
droppable!();
assert_eq!(Droppable::count(), 0);
{
let _m: Maybe<Droppable> = Maybe::none();
}
assert_eq!(Droppable::count(), 0);
{
let _m: Maybe<Droppable> = Maybe::some(Droppable::new());
}
assert_eq!(Droppable::count(), 0);
{
let m: Maybe<Droppable> = Maybe::some(Droppable::new());
m.into_option();
}
assert_eq!(Droppable::count(), 0);
{
let m: Maybe<Droppable> = Maybe::some(Droppable::new());
let _m2 = m.clone();
core::mem::drop(m);
assert_eq!(Droppable::count(), 1);
}
assert_eq!(Droppable::count(), 0);
{
let mut m: Maybe<Droppable> = Maybe::some(Droppable::new());
m.clear();
}
assert_eq!(Droppable::count(), 0);
}
}