use std::cell::Cell;
use std::rc::{Rc, Weak};
pub trait CellOption {
fn is_none(&self) -> bool;
}
impl<T> CellOption for Cell<Option<T>> {
#[inline]
fn is_none(&self) -> bool {
#[cfg(not(feature = "safe"))]
#[allow(unsafe_code)]
{
unsafe { (*self.as_ptr()).is_none() }
}
#[cfg(feature = "safe")]
{
let value = self.take();
let result = value.is_none();
self.set(value);
result
}
}
}
pub trait CellOptionWeak<T> {
fn upgrade(&self) -> Option<Rc<T>>;
fn clone_inner(&self) -> Option<Weak<T>>;
}
impl<T> CellOptionWeak<T> for Cell<Option<Weak<T>>> {
#[inline]
fn upgrade(&self) -> Option<Rc<T>> {
#[cfg(not(feature = "safe"))]
#[allow(unsafe_code)]
{
unsafe { (*self.as_ptr()).as_ref().and_then(Weak::upgrade) }
}
#[cfg(feature = "safe")]
{
let value = self.take();
let result = value.as_ref().and_then(Weak::upgrade);
self.set(value);
result
}
}
#[inline]
fn clone_inner(&self) -> Option<Weak<T>> {
#[cfg(not(feature = "safe"))]
#[allow(unsafe_code)]
{
unsafe { (*self.as_ptr()).clone() }
}
#[cfg(feature = "safe")]
{
let value = self.take();
let result = value.clone();
self.set(value);
result
}
}
}
pub trait CellOptionRc<T> {
fn take_if_unique_strong(&self) -> Option<Rc<T>>;
fn clone_inner(&self) -> Option<Rc<T>>;
}
impl<T> CellOptionRc<T> for Cell<Option<Rc<T>>> {
#[inline]
fn take_if_unique_strong(&self) -> Option<Rc<T>> {
#[cfg(not(feature = "safe"))]
#[allow(unsafe_code)]
{
unsafe {
match *self.as_ptr() {
None => None,
Some(ref rc) if Rc::strong_count(rc) > 1 => None,
Some(_) => self.take(),
}
}
}
#[cfg(feature = "safe")]
{
let value = self.take();
let result = match value {
None => None,
Some(ref rc) if Rc::strong_count(rc) > 1 => {
self.set(value);
None
}
Some(rc) => Some(rc),
};
result
}
}
#[inline]
fn clone_inner(&self) -> Option<Rc<T>> {
#[cfg(not(feature = "safe"))]
#[allow(unsafe_code)]
{
unsafe { (*self.as_ptr()).clone() }
}
#[cfg(feature = "safe")]
{
let value = self.take();
let result = value.clone();
self.set(value);
result
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
use std::rc::{Rc, Weak};
#[test]
fn cell_option_is_none_with_none() {
let cell: Cell<Option<i32>> = Cell::new(None);
assert!(cell.is_none());
}
#[test]
fn cell_option_is_none_with_some() {
let cell = Cell::new(Some(42));
assert!(!cell.is_none());
}
#[test]
fn cell_option_weak_upgrade_some() {
let rc = Rc::new(42);
let weak = Rc::downgrade(&rc);
let cell = Cell::new(Some(weak));
let upgraded = cell.upgrade();
assert!(upgraded.is_some());
assert_eq!(*upgraded.unwrap(), 42);
}
#[test]
fn cell_option_weak_upgrade_none() {
let cell: Cell<Option<Weak<i32>>> = Cell::new(None);
assert!(cell.upgrade().is_none());
}
#[test]
fn cell_option_weak_clone_inner() {
let rc = Rc::new(42);
let weak = Rc::downgrade(&rc);
let cell = Cell::new(Some(weak));
let cloned = cell.clone_inner();
assert!(cloned.is_some());
assert_eq!(*cloned.unwrap().upgrade().unwrap(), 42);
}
#[test]
fn cell_option_rc_take_if_unique_strong() {
let rc = Rc::new(42);
let cell = Cell::new(Some(rc));
let taken = cell.take_if_unique_strong();
assert!(taken.is_some());
assert_eq!(*taken.unwrap(), 42);
assert!(cell.take().is_none());
}
#[test]
fn cell_option_rc_take_if_unique_strong_multiple_refs() {
let rc = Rc::new(42);
let rc2 = rc.clone();
let cell = Cell::new(Some(rc));
let taken = cell.take_if_unique_strong();
assert!(taken.is_none());
assert!(cell.take().is_some());
drop(rc2);
}
#[test]
fn cell_option_rc_clone_inner() {
let rc = Rc::new(42);
let cell = Cell::new(Some(rc));
let cloned = cell.clone_inner();
assert!(cloned.is_some());
assert_eq!(*cloned.unwrap(), 42);
}
}