#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
use std::mem;
#[cfg(feature = "sync")]
#[doc(hidden)]
pub use std::sync::{Arc as Strong, Weak};
#[cfg(not(feature = "sync"))]
#[doc(hidden)]
pub use std::rc::{Rc as Strong, Weak};
#[derive(Debug)]
pub enum RCell<T> {
Strong(Strong<T>),
Weak(Weak<T>),
Empty,
}
impl<T> RCell<T> {
pub fn new(value: T) -> Self {
RCell::Strong(Strong::new(value))
}
pub fn retained(&self) -> bool {
matches!(*self, RCell::Strong(_))
}
pub fn refcount(&self) -> usize {
match self {
RCell::Strong(arc) => Strong::strong_count(arc),
RCell::Weak(weak) => weak.strong_count(),
RCell::Empty => 0,
}
}
pub fn retain(&mut self) -> Option<Strong<T>> {
match self {
RCell::Strong(strong) => Some(strong.clone()),
RCell::Weak(weak) => {
if let Some(strong) = weak.upgrade() {
let _ = mem::replace(self, RCell::Strong(strong.clone()));
Some(strong)
} else {
None
}
}
RCell::Empty => None,
}
}
pub fn release(&mut self) {
if let Some(weak) = match self {
RCell::Strong(strong) => Some(Strong::downgrade(strong)),
RCell::Weak(weak) => Some(weak.clone()),
RCell::Empty => None,
} {
if weak.strong_count() > 0 {
let _ = mem::replace(self, RCell::Weak(weak));
} else {
let _ = mem::replace(self, RCell::Empty);
}
}
}
pub fn remove(&mut self) {
let _ = mem::replace(self, RCell::Empty);
}
pub fn request(&self) -> Option<Strong<T>> {
match self {
RCell::Strong(arc) => Some(arc.clone()),
RCell::Weak(weak) => weak.upgrade(),
RCell::Empty => None,
}
}
}
pub trait Replace<T> {
fn replace(&mut self, new: T);
}
impl<T> Replace<Strong<T>> for RCell<T> {
fn replace(&mut self, strong: Strong<T>) {
let _ = mem::replace(self, RCell::Strong(strong));
}
}
impl<T> Replace<Weak<T>> for RCell<T> {
fn replace(&mut self, weak: Weak<T>) {
let _ = mem::replace(self, RCell::Weak(weak));
}
}
impl<T> From<Strong<T>> for RCell<T> {
fn from(strong: Strong<T>) -> Self {
RCell::Strong(strong)
}
}
impl<T> From<Weak<T>> for RCell<T> {
fn from(weak: Weak<T>) -> Self {
RCell::Weak(weak)
}
}
impl<T> Default for RCell<T> {
fn default() -> Self {
RCell::Empty
}
}
#[cfg(test)]
mod tests {
use crate::{RCell, Strong, Replace};
#[test]
fn smoke() {
let rcell = RCell::new("foobar");
assert!(rcell.retained());
}
#[test]
fn new() {
let mut rcell = RCell::new("foobar");
assert!(rcell.retained());
assert_eq!(*rcell.request().unwrap(), "foobar");
rcell.release();
assert_eq!(rcell.request(), None);
}
#[test]
fn default() {
let rcell = RCell::<i32>::default();
assert!(!rcell.retained());
assert_eq!(rcell.request(), None);
}
#[test]
fn from_strong() {
let strong = Strong::new("foobar");
let mut rcell = RCell::from(strong);
assert!(rcell.retained());
assert_eq!(*rcell.request().unwrap(), "foobar");
rcell.release();
assert_eq!(rcell.request(), None);
}
#[test]
fn from_weak_release() {
let strong = Strong::new("foobar");
let weak = Strong::downgrade(&strong);
let mut rcell = RCell::from(weak);
assert!(!rcell.retained());
assert_eq!(*rcell.request().unwrap(), "foobar");
rcell.release();
assert_eq!(*rcell.request().unwrap(), "foobar");
rcell.remove();
assert_eq!(rcell.request(), None);
}
#[test]
fn from_weak_drop_original() {
let strong = Strong::new("foobar");
let weak = Strong::downgrade(&strong);
let mut rcell = RCell::from(weak);
assert!(!rcell.retained());
assert_eq!(*rcell.request().unwrap(), "foobar");
drop(strong);
assert_eq!(rcell.request(), None);
rcell.remove();
assert_eq!(rcell.request(), None);
}
#[test]
fn replace_strong() {
let mut rcell = RCell::default();
assert!(!rcell.retained());
rcell.replace(Strong::new("foobar"));
assert_eq!(*rcell.request().unwrap(), "foobar");
rcell.remove();
assert_eq!(rcell.request(), None);
}
#[test]
fn replace_weak() {
let strong = Strong::new("foobar");
let mut rcell = RCell::default();
assert!(!rcell.retained());
rcell.replace(Strong::downgrade(&strong));
assert_eq!(*rcell.request().unwrap(), "foobar");
rcell.remove();
assert_eq!(rcell.request(), None);
}
}