use alloc::collections::VecDeque;
use alloc::fmt;
use core::{
future::Future,
ops::{Deref, DerefMut},
pin::Pin,
task::{Context, Poll, Waker},
};
use fairly_unsafe_cell::FairlyUnsafeCell;
pub struct TakeCell<T> {
value: FairlyUnsafeCell<Option<T>>,
parked: FairlyUnsafeCell<VecDeque<Waker>>, }
impl<T> Default for TakeCell<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> TakeCell<T> {
pub fn new() -> Self {
TakeCell {
value: FairlyUnsafeCell::new(None),
parked: FairlyUnsafeCell::new(VecDeque::new()),
}
}
pub fn new_with(value: T) -> Self {
TakeCell {
value: FairlyUnsafeCell::new(Some(value)),
parked: FairlyUnsafeCell::new(VecDeque::new()),
}
}
pub fn into_inner(self) -> Option<T> {
self.value.into_inner()
}
pub fn is_empty(&self) -> bool {
let r = unsafe { self.value.borrow() };
r.deref().is_none()
}
pub fn set(&self, value: T) {
let _ = self.replace(value);
}
pub fn replace(&self, value: T) -> Option<T> {
let mut r = unsafe { self.value.borrow_mut() };
match r.deref_mut().take() {
None => {
*r.deref_mut() = Some(value);
self.wake_next();
None
}
Some(old) => {
*r.deref_mut() = Some(value);
Some(old)
}
}
}
pub fn try_take(&self) -> Option<T> {
let mut r = unsafe { self.value.borrow_mut() };
r.deref_mut().take()
}
pub async fn take(&self) -> T {
TakeFuture(self).await
}
pub fn update(&self, with: impl FnOnce(Option<T>) -> T) {
self.set(with(self.try_take()))
}
pub fn fallible_update<E>(
&self,
with: impl FnOnce(Option<T>) -> Result<T, E>,
) -> Result<(), E> {
self.set(with(self.try_take())?);
Ok(())
}
pub fn count_waiting(&self) -> usize {
let parked = unsafe { self.parked.borrow() };
parked.deref().len()
}
fn wake_next(&self) {
let mut parked = unsafe { self.parked.borrow_mut() };
if let Some(waker) = parked.deref_mut().pop_front() {
waker.wake()
}
}
fn park(&self, cx: &mut Context<'_>) {
let mut parked = unsafe { self.parked.borrow_mut() };
parked.deref_mut().push_back(cx.waker().clone());
}
}
impl<T: Clone> TakeCell<T> {
pub fn peek(&self) -> Option<T> {
let r = unsafe { self.value.borrow_mut() };
r.deref().clone()
}
}
impl<T: fmt::Debug> fmt::Debug for TakeCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_tuple("TakeCell");
match unsafe { self.value.borrow().deref() } {
Some(t) => {
d.field(t);
}
None => {
d.field(&format_args!("<empty>"));
}
}
d.finish()
}
}
struct TakeFuture<'cell, T>(&'cell TakeCell<T>);
impl<T> Future for TakeFuture<'_, T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.0.try_take() {
Some(t) => Poll::Ready(t),
None => {
self.0.park(cx);
Poll::Pending
}
}
}
}