use std::{
cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut},
rc::{Rc, Weak},
};
#[repr(transparent)]
#[derive(Debug)]
pub struct SharedCell<T>(Rc<RefCell<T>>);
impl<T> Clone for SharedCell<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T> SharedCell<T> {
#[inline]
pub fn new(value: T) -> Self {
Self(Rc::new(RefCell::new(value)))
}
#[inline]
#[must_use]
pub fn downgrade(&self) -> WeakCell<T> {
WeakCell(Rc::downgrade(&self.0))
}
#[inline]
#[must_use]
pub fn borrow(&self) -> Ref<'_, T> {
self.0.borrow()
}
#[inline]
#[must_use]
pub fn borrow_mut(&self) -> RefMut<'_, T> {
self.0.borrow_mut()
}
#[inline]
pub fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
self.0.try_borrow()
}
#[inline]
pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
self.0.try_borrow_mut()
}
#[inline]
#[must_use]
pub fn strong_count(&self) -> usize {
Rc::strong_count(&self.0)
}
#[inline]
#[must_use]
pub fn weak_count(&self) -> usize {
Rc::weak_count(&self.0)
}
}
impl<T> From<Rc<RefCell<T>>> for SharedCell<T> {
fn from(inner: Rc<RefCell<T>>) -> Self {
Self(inner)
}
}
impl<T> From<SharedCell<T>> for Rc<RefCell<T>> {
fn from(shared: SharedCell<T>) -> Self {
shared.0
}
}
impl<T> std::ops::Deref for SharedCell<T> {
type Target = Rc<RefCell<T>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[repr(transparent)]
#[derive(Debug)]
pub struct WeakCell<T>(Weak<RefCell<T>>);
impl<T> Clone for WeakCell<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T> WeakCell<T> {
#[inline]
pub fn upgrade(&self) -> Option<SharedCell<T>> {
self.0.upgrade().map(SharedCell)
}
#[inline]
#[must_use]
pub fn is_dropped(&self) -> bool {
self.0.strong_count() == 0
}
}
impl<T> From<Weak<RefCell<T>>> for WeakCell<T> {
fn from(inner: Weak<RefCell<T>>) -> Self {
Self(inner)
}
}
impl<T> From<WeakCell<T>> for Weak<RefCell<T>> {
fn from(cell: WeakCell<T>) -> Self {
cell.0
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
fn test_shared_cell_new_and_borrow() {
let cell = SharedCell::new(42);
assert_eq!(*cell.borrow(), 42);
}
#[rstest]
fn test_shared_cell_borrow_mut() {
let cell = SharedCell::new(0);
*cell.borrow_mut() = 99;
assert_eq!(*cell.borrow(), 99);
}
#[rstest]
fn test_shared_cell_clone_shares_value() {
let cell = SharedCell::new(10);
let clone = cell.clone();
*cell.borrow_mut() = 20;
assert_eq!(*clone.borrow(), 20);
}
#[rstest]
fn test_shared_cell_strong_weak_counts() {
let cell = SharedCell::new(1);
assert_eq!(cell.strong_count(), 1);
assert_eq!(cell.weak_count(), 0);
let weak = cell.downgrade();
assert_eq!(cell.weak_count(), 1);
assert_eq!(cell.strong_count(), 1);
let clone = cell.clone();
assert_eq!(cell.strong_count(), 2);
drop(clone);
assert_eq!(cell.strong_count(), 1);
drop(weak);
assert_eq!(cell.weak_count(), 0);
}
#[rstest]
fn test_weak_cell_upgrade_succeeds_while_alive() {
let cell = SharedCell::new(10);
let weak = cell.downgrade();
assert!(!weak.is_dropped());
let upgraded = weak.upgrade();
assert!(upgraded.is_some());
assert_eq!(*upgraded.unwrap().borrow(), 10);
}
#[rstest]
fn test_weak_cell_upgrade_fails_after_drop() {
let weak = {
let cell = SharedCell::new(10);
cell.downgrade()
};
assert!(weak.is_dropped());
assert!(weak.upgrade().is_none());
}
#[rstest]
#[expect(clippy::redundant_clone, reason = "Clone is the behavior under test")]
fn test_weak_cell_clone() {
let cell = SharedCell::new(5);
let weak1 = cell.downgrade();
let weak2 = weak1.clone();
assert_eq!(cell.weak_count(), 2);
assert_eq!(*weak2.upgrade().unwrap().borrow(), 5);
}
#[rstest]
fn test_try_borrow_fails_while_mutably_borrowed() {
let cell = SharedCell::new(0);
let _guard = cell.borrow_mut();
assert!(cell.try_borrow().is_err());
}
#[rstest]
fn test_try_borrow_mut_fails_while_borrowed() {
let cell = SharedCell::new(0);
let _guard = cell.borrow();
assert!(cell.try_borrow_mut().is_err());
}
#[rstest]
fn test_from_rc_refcell_roundtrip() {
let rc = Rc::new(RefCell::new(5));
let cell = SharedCell::from(rc);
assert_eq!(*cell.borrow(), 5);
let back: Rc<RefCell<i32>> = cell.into();
assert_eq!(*back.borrow(), 5);
}
#[rstest]
fn test_from_weak_refcell_roundtrip() {
let rc = Rc::new(RefCell::new(7));
let weak_cell = WeakCell::from(Rc::downgrade(&rc));
assert_eq!(*weak_cell.upgrade().unwrap().borrow(), 7);
let back: Weak<RefCell<i32>> = weak_cell.into();
assert_eq!(*back.upgrade().unwrap().borrow(), 7);
}
}