use std::ops::{Deref, DerefMut};
pub enum OfferEntry<'a, T> {
Occupied(Offering<'a, T>),
Vacant(EmptyOffering<'a, T>),
}
pub struct EmptyOffering<'a, T> {
item: &'a mut Option<T>,
}
impl<'a, T> EmptyOffering<'a, T> {
pub fn insert(self, item: T) -> Offering<'a, T> {
*self.item = Some(item);
Offering { item: self.item }
}
}
pub struct Offering<'a, T> {
item: &'a mut Option<T>,
}
impl<'a, T> Deref for Offering<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match &self.item {
Some(item) => item,
_ => unreachable!(),
}
}
}
impl<'a, T> DerefMut for Offering<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
match &mut self.item {
Some(item) => item,
_ => unreachable!(),
}
}
}
impl<'a, T> Offering<'a, T> {
pub fn take(self) -> T {
match self.item.take() {
Some(item) => item,
None => unreachable!(),
}
}
}
pub struct OfferCell<T> {
item: Option<T>,
}
impl<T> Default for OfferCell<T> {
fn default() -> Self {
Self::empty()
}
}
impl<T> OfferCell<T> {
pub fn empty() -> Self {
Self { item: None }
}
pub fn new(item: T) -> Self {
Self { item: Some(item) }
}
pub fn is_empty(&self) -> bool {
self.item.is_none()
}
pub fn item(&self) -> Option<&T> {
self.item.as_ref()
}
pub fn item_mut(&mut self) -> Option<&mut T> {
self.item.as_mut()
}
pub fn take(&mut self) -> Option<T> {
self.item.take()
}
pub fn offer(&mut self) -> Option<Offering<T>> {
if self.item.is_none() {
return None;
}
Some(Offering {
item: &mut self.item,
})
}
pub fn insert_offer(&mut self, item: T) -> Offering<T> {
self.item = Some(item);
Offering {
item: &mut self.item,
}
}
pub fn offer_or_insert(&mut self, mut insert: impl FnMut() -> T) -> Offering<T> {
if self.item.is_none() {
self.item = Some(insert());
}
Offering {
item: &mut self.item,
}
}
pub fn entry(&mut self) -> OfferEntry<T> {
match self.item.is_some() {
true => OfferEntry::Occupied(Offering {
item: &mut self.item,
}),
false => OfferEntry::Vacant(EmptyOffering {
item: &mut self.item,
}),
}
}
}