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;
use crate::extend_lifetime;
pub struct OnceCell<T> {
state: FairlyUnsafeCell<State<T>>, }
enum State<T> {
Set(T),
Empty(VecDeque<Waker>),
}
impl<T> Default for OnceCell<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> OnceCell<T> {
pub fn new() -> Self {
OnceCell {
state: FairlyUnsafeCell::new(State::Empty(VecDeque::new())),
}
}
pub fn new_with(t: T) -> Self {
OnceCell {
state: FairlyUnsafeCell::new(State::Set(t)),
}
}
pub fn into_inner(self) -> Option<T> {
match self.state.into_inner() {
State::Empty(_) => None,
State::Set(t) => Some(t),
}
}
pub fn is_empty(&self) -> bool {
match unsafe { self.state.borrow().deref() } {
State::Empty(_) => true,
State::Set(_) => false,
}
}
pub fn try_get_mut(&mut self) -> Option<&mut T> {
match self.state.get_mut() {
State::Empty(_) => None,
State::Set(ref mut t) => Some(t),
}
}
pub fn try_get(&self) -> Option<&T> {
match unsafe { self.state.borrow().deref() } {
State::Empty(_) => None,
State::Set(ref t) => Some(unsafe {
extend_lifetime(t)
}),
}
}
pub async fn get(&self) -> &T {
GetFuture(self).await
}
pub fn set(&self, t: T) -> Result<(), T> {
match unsafe { self.state.borrow_mut().deref_mut() } {
State::Empty(queue) => {
for waker in queue.iter() {
waker.wake_by_ref();
}
}
State::Set(_) => return Err(t),
}
unsafe {
*self.state.borrow_mut().deref_mut() = State::Set(t);
}
Ok(())
}
}
impl<T: fmt::Debug> fmt::Debug for OnceCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_tuple("OnceCell");
match self.try_get() {
Some(t) => {
d.field(t);
}
None => {
d.field(&format_args!("<empty>"));
}
}
d.finish()
}
}
struct GetFuture<'cell, T>(&'cell OnceCell<T>);
impl<'cell, T> Future for GetFuture<'cell, T> {
type Output = &'cell T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match unsafe { &mut self.0.state.borrow_mut().deref_mut() } {
State::Empty(queue) => {
queue.push_back(cx.waker().clone());
Poll::Pending
}
State::Set(ref t) => Poll::Ready(unsafe {
extend_lifetime(t)
}),
}
}
}