#![cfg_attr(not(test), no_std)]
#![allow(rustdoc::redundant_explicit_links)]
extern crate alloc;
use alloc::boxed::Box;
use core::cell::Cell;
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
pub(crate) mod pointer;
use pointer::Pointer;
pub mod errors;
use errors::{BorrowError, BorrowMutError, ConsumeError};
#[derive(Debug)]
pub struct FlexCell<T: ?Sized>(Cell<Pointer<T>>);
impl<T: ?Sized> Drop for FlexCell<T> {
#[inline]
fn drop(&mut self) {
drop(self.0.get().into_guard());
}
}
struct Guard<'a, T: ?Sized>(&'a Cell<Pointer<T>>, Pointer<T>);
impl<'a, T: ?Sized> Guard<'a, T> {
fn create<R>(cell: &'a Cell<Pointer<T>>, pointer: Pointer<T>, f: impl FnOnce() -> R) -> R {
let guard = Self(cell, cell.replace(pointer));
let result = f();
drop(guard);
result
}
}
impl<T: ?Sized> Drop for Guard<'_, T> {
#[inline]
fn drop(&mut self) {
drop(self.0.replace(self.1).into_guard());
}
}
impl<T> FlexCell<T> {
#[inline]
pub fn new(value: T) -> Self {
Self::from_boxed(Box::new(value))
}
#[inline]
#[track_caller]
pub fn into_inner(self) -> T {
self.try_into_inner().unwrap()
}
#[inline]
pub fn set(&self, value: T) {
self.set_boxed(Box::new(value));
}
#[inline]
#[track_caller]
pub fn take(&self) -> T {
self.try_take().unwrap()
}
#[inline]
#[track_caller]
#[must_use = "If you don't need the old value, consider using `set` instead."]
pub fn replace(&self, value: T) -> T {
self.try_replace(value).unwrap()
}
#[inline]
pub fn try_into_inner(self) -> Result<T, ConsumeError> {
self.try_into_boxed().map(|value| *value)
}
#[inline]
pub fn try_take(&self) -> Result<T, ConsumeError> {
self.try_take_boxed().map(|value| *value)
}
#[inline]
#[must_use = "If you don't need the old value, consider using `set` instead."]
pub fn try_replace(&self, value: T) -> Result<T, ConsumeError> {
self.try_replace_boxed(Box::new(value)).map(|value| *value)
}
}
impl<T: ?Sized> FlexCell<T> {
pub fn as_ptr(&self) -> Option<NonNull<T>> {
match self.0.get() {
Pointer::Ref(ptr) | Pointer::Mut(ptr) | Pointer::Boxed(ptr) => Some(ptr),
Pointer::Null => None,
}
}
pub const fn empty() -> Self {
Self(Cell::new(Pointer::Null))
}
pub fn from_boxed(value: Box<T>) -> Self {
Self(Cell::new(Pointer::from_boxed(value)))
}
#[inline]
#[track_caller]
pub fn into_boxed(self) -> Box<T> {
self.try_into_boxed().unwrap()
}
pub fn set_boxed(&self, value: Box<T>) {
drop(self.0.replace(Pointer::from_boxed(value)).into_guard());
}
#[inline]
#[track_caller]
pub fn take_boxed(&self) -> Box<T> {
self.try_take_boxed().unwrap()
}
#[inline]
#[track_caller]
#[must_use = "If you don't need the old value, consider using `set_boxed` instead."]
pub fn replace_boxed(&self, value: Box<T>) -> Box<T> {
self.try_replace_boxed(value).unwrap()
}
pub fn try_into_boxed(self) -> Result<Box<T>, ConsumeError> {
if let Pointer::Boxed(ptr) = ManuallyDrop::new(self).0.get() {
Ok(unsafe { Box::from_raw(ptr.as_ptr()) })
} else {
Err(ConsumeError)
}
}
pub fn try_take_boxed(&self) -> Result<Box<T>, ConsumeError> {
if let Pointer::Boxed(ptr) = self.0.get() {
let value = unsafe { Box::from_raw(ptr.as_ptr()) };
self.0.set(Pointer::Null);
Ok(value)
} else {
Err(ConsumeError)
}
}
#[must_use = "If you don't need the old value, consider using `set_boxed` instead."]
pub fn try_replace_boxed(&self, value: Box<T>) -> Result<Box<T>, ConsumeError> {
if let Pointer::Boxed(ptr) = self.0.get() {
self.0.set(Pointer::from_boxed(value));
Ok(unsafe { Box::from_raw(ptr.as_ptr()) })
} else {
Err(ConsumeError)
}
}
}
impl<T: ?Sized> FlexCell<T> {
#[inline]
pub fn lend<R>(&self, value: &mut T, f: impl FnOnce() -> R) -> R {
Guard::create(&self.0, Pointer::from_mut(value), f)
}
#[inline]
pub fn lend_ref<R>(&self, value: &T, f: impl FnOnce() -> R) -> R {
Guard::create(&self.0, Pointer::from_ref(value), f)
}
#[inline]
#[track_caller]
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
self.try_with(f).unwrap()
}
#[inline]
#[track_caller]
pub fn with_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
self.try_with_mut(f).unwrap()
}
pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R, BorrowError> {
if let Pointer::Ref(ptr) | Pointer::Mut(ptr) | Pointer::Boxed(ptr) = self.0.get() {
Guard::create(&self.0, Pointer::Ref(ptr), move || {
Ok(f(unsafe { ptr.as_ref() }))
})
} else {
Err(BorrowError)
}
}
pub fn try_with_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R, BorrowMutError> {
if let Pointer::Mut(mut ptr) | Pointer::Boxed(mut ptr) = self.0.get() {
Guard::create(&self.0, Pointer::Null, move || {
Ok(f(unsafe { ptr.as_mut() }))
})
} else {
Err(BorrowMutError)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn borrow_ref() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with(|value| {
assert_eq!(*value, vec![0, 0, 0]);
});
}
#[test]
fn borrow_mut() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with_mut(|value| {
assert_eq!(*value, vec![0, 0, 0]);
value.push(1);
});
cell.with(|value| assert_eq!(*value, vec![0, 0, 0, 1]));
}
#[test]
fn set() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.set(vec![1, 2, 3]);
cell.with_mut(|value| assert_eq!(*value, vec![1, 2, 3]));
}
#[test]
fn replace() {
let cell = FlexCell::new(vec![0, 0, 0]);
let value = cell.replace(vec![1, 2, 3]);
assert_eq!(value, vec![0, 0, 0]);
cell.with_mut(|value| assert_eq!(*value, vec![1, 2, 3]));
}
#[test]
fn take() {
let cell = FlexCell::new(vec![0, 0, 0]);
let _ = cell.take();
cell.try_with(|_| {}).unwrap_err();
}
#[test]
fn borrow_ref_and_lend_ref_with_self() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with(|value| {
cell.lend_ref(value, || {
cell.with(|value| assert_eq!(*value, vec![0, 0, 0]));
});
});
}
#[test]
fn borrow_ref_and_lend_ref_with_other() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with(|_| {
let other = vec![1, 2, 3];
cell.lend_ref(&other, || {
cell.with(|value| assert_eq!(*value, vec![1, 2, 3]));
});
});
cell.with(|value| assert_eq!(*value, vec![0, 0, 0]));
}
#[test]
fn borrow_ref_and_set() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with(|_| {
cell.set(vec![1, 2, 3]);
cell.with(|value| assert_eq!(*value, vec![1, 2, 3]));
});
cell.with(|value| assert_eq!(*value, vec![0, 0, 0]));
}
#[test]
#[should_panic = "ConsumeError"]
fn borrow_ref_and_take() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with(|_| cell.take());
}
#[test]
#[should_panic = "ConsumeError"]
fn borrow_ref_and_replace() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with(|_| cell.replace(vec![1, 2, 3]));
}
#[test]
fn borrow_ref_and_lend_mut_with_other() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with(|value| {
let mut other = vec![1, 2, 3];
cell.lend(&mut other, || {
cell.with(|value| assert_eq!(*value, vec![1, 2, 3]));
cell.with_mut(|value| value.push(4));
});
assert_eq!(other, vec![1, 2, 3, 4]);
assert_eq!(*value, vec![0, 0, 0]);
});
cell.with(|value| assert_eq!(*value, vec![0, 0, 0]));
}
#[test]
fn borrow_mut_and_lend_ref_with_self() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with_mut(|value| {
value.push(1);
cell.lend_ref(value, || {
cell.with(|value| assert_eq!(*value, vec![0, 0, 0, 1]));
cell.try_with_mut(|_| {}).unwrap_err();
});
assert_eq!(*value, vec![0, 0, 0, 1]);
});
cell.with(|value| assert_eq!(*value, vec![0, 0, 0, 1]));
}
#[test]
fn borrow_mut_and_lend_ref_with_other() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with_mut(|value| {
value.push(1);
let other = vec![1, 2, 3];
cell.lend_ref(&other, || {
cell.with(|value| assert_eq!(*value, vec![1, 2, 3]));
cell.try_with_mut(|_| {}).unwrap_err();
});
assert_eq!(*value, vec![0, 0, 0, 1]);
});
cell.with(|value| assert_eq!(*value, vec![0, 0, 0, 1]));
}
#[test]
fn borrow_mut_and_lend_mut_with_self() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with_mut(|value| {
value.push(1);
cell.lend(value, || {
cell.with_mut(|value| assert_eq!(*value, vec![0, 0, 0, 1]));
});
assert_eq!(*value, vec![0, 0, 0, 1]);
});
cell.with(|value| assert_eq!(*value, vec![0, 0, 0, 1]));
}
#[test]
fn borrow_mut_and_lend_mut_with_other() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with_mut(|value| {
value.push(1);
let mut other = vec![1, 2, 3];
cell.lend(&mut other, || {
cell.with(|value| assert_eq!(*value, vec![1, 2, 3]));
cell.with_mut(|value| value.push(4));
});
assert_eq!(other, vec![1, 2, 3, 4]);
});
cell.with(|value| assert_eq!(*value, vec![0, 0, 0, 1]));
}
#[test]
fn borrow_mut_and_set() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with_mut(|_| {
cell.set(vec![1, 2, 3]);
cell.with_mut(|value| assert_eq!(*value, vec![1, 2, 3]));
});
cell.with(|value| assert_eq!(*value, vec![0, 0, 0]));
}
#[test]
#[should_panic = "ConsumeError"]
fn borrow_mut_and_take() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with_mut(|_| cell.take());
}
#[test]
#[should_panic = "ConsumeError"]
fn borrow_mut_and_replace() {
let cell = FlexCell::new(vec![0, 0, 0]);
cell.with_mut(|_| cell.replace(vec![1, 2, 3]));
}
#[test]
fn take_and_lend_ref_with_self() {
let cell = FlexCell::new(vec![0, 0, 0]);
let value = cell.take();
cell.lend_ref(&value, || {
cell.with(|value| assert_eq!(*value, vec![0, 0, 0]));
cell.try_with_mut(|_| {}).unwrap_err();
});
cell.try_with(|_| {}).unwrap_err();
}
#[test]
fn take_and_lend_ref_with_other() {
let cell = FlexCell::new(vec![0, 0, 0]);
let _ = cell.take();
let other = vec![1, 2, 3];
cell.lend_ref(&other, || {
cell.with(|value| assert_eq!(*value, vec![1, 2, 3]));
cell.try_with_mut(|_| {}).unwrap_err();
});
cell.try_with(|_| {}).unwrap_err();
}
#[test]
fn take_and_lend_mut_with_self() {
let cell = FlexCell::new(vec![0, 0, 0]);
let mut value = cell.take();
cell.lend(&mut value, || {
cell.with_mut(|value| assert_eq!(*value, vec![0, 0, 0]));
});
cell.try_with(|_| {}).unwrap_err();
}
#[test]
fn take_and_lend_mut_with_other() {
let cell = FlexCell::new(vec![0, 0, 0]);
let _ = cell.take();
let mut other = vec![1, 2, 3];
cell.lend(&mut other, || {
cell.with_mut(|value| assert_eq!(*value, vec![1, 2, 3]));
});
cell.try_with(|_| {}).unwrap_err();
}
}