use std::marker::PhantomPinned;
use std::ptr::NonNull;
use std::{cell::OnceCell, pin::Pin};
pub struct PacCell<P, C>(Pin<Box<PacInner<P, C>>>);
struct PacInner<P, C> {
child: OnceCell<C>,
parent: P,
_pin: PhantomPinned,
}
impl<'p, P: 'p, C> PacCell<P, C> {
pub fn new<F>(parent: P, child_constructor: F) -> Self
where
F: FnOnce(&'p mut P) -> C,
{
Self::try_new::<_, ()>(parent, |p| Ok(child_constructor(p))).unwrap()
}
pub fn try_new<F, E>(parent: P, child_constructor: F) -> Result<Self, E>
where
F: FnOnce(&'p mut P) -> Result<C, E>,
{
let inner = PacInner {
parent,
child: OnceCell::new(),
_pin: PhantomPinned,
};
let mut inner = Box::pin(inner);
let mut parent_ref = NonNull::from(&inner.as_mut().parent);
let parent_ref = unsafe { parent_ref.as_mut() };
let child = child_constructor(parent_ref)?;
let _ = inner.child.set(child);
Ok(PacCell(inner))
}
pub fn with_mut<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut C) -> R,
{
let mut_ref: Pin<&mut PacInner<P, C>> = Pin::as_mut(&mut self.0);
let inner = unsafe { Pin::get_unchecked_mut(mut_ref) };
let fuel = inner.child.get_mut().unwrap();
f(fuel)
}
pub fn unwrap(self) -> P {
let inner = unsafe { Pin::into_inner_unchecked(self.0) };
inner.parent
}
}