use std::cell::BorrowError;
use std::cell::Cell;
use std::cell::Ref;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::fmt;
use std::fmt::Display;
use std::hash::Hash;
use std::hash::Hasher;
use std::mem;
use std::ops::Deref;
use crate::cast;
#[derive(Debug)]
pub struct ARef<'a, T: ?Sized + 'a> {
value: &'a T,
borrow: Option<&'a Cell<isize>>,
}
impl<T: ?Sized> Drop for ARef<'_, T> {
fn drop(&mut self) {
if self.borrow.is_some() {
let me: ARef<T> = ARef {
value: self.value,
borrow: self.borrow,
};
let them: Ref<T> = unsafe { cast::transmute_unchecked(me) };
mem::drop(them);
}
}
}
impl<T: ?Sized> Deref for ARef<'_, T> {
type Target = T;
fn deref(&self) -> &T {
self.value
}
}
impl<'a, T: ?Sized + 'a> ARef<'a, T> {
pub fn new_ptr(x: &'a T) -> Self {
ARef {
value: x,
borrow: None,
}
}
pub fn new_ref(x: Ref<'a, T>) -> Self {
let v: ARef<T> = unsafe { cast::transmute_unchecked(x) };
debug_assert!(v.borrow.is_some());
v
}
#[allow(clippy::should_implement_trait)]
pub fn clone(orig: &Self) -> Self {
if orig.borrow.is_none() {
ARef::new_ptr(orig.value)
} else {
let orig: &Ref<T> = unsafe { cast::ptr(orig) };
Self::new_ref(Ref::clone(orig))
}
}
pub fn map<U: ?Sized, F>(orig: ARef<'a, T>, f: F) -> ARef<'a, U>
where
F: FnOnce(&T) -> &U,
{
let res = ARef {
value: f(orig.value),
borrow: orig.borrow,
};
#[allow(clippy::mem_forget)]
mem::forget(orig);
res
}
pub fn map_split<U: ?Sized, V: ?Sized, F>(orig: ARef<'a, T>, f: F) -> (ARef<'a, U>, ARef<'a, V>)
where
F: FnOnce(&T) -> (&U, &V),
{
if orig.borrow.is_none() {
let (a, b) = f(orig.value);
(ARef::new_ptr(a), ARef::new_ptr(b))
} else {
let orig: Ref<T> = unsafe { cast::transmute_unchecked(orig) };
let (a, b) = Ref::map_split(orig, f);
(ARef::new_ref(a), ARef::new_ref(b))
}
}
pub fn filter_map<U: ?Sized, F>(orig: ARef<'a, T>, f: F) -> Result<ARef<'a, U>, Self>
where
F: FnOnce(&T) -> Option<&U>,
{
match f(orig.value) {
Some(value) => {
let res = Ok(ARef {
value,
borrow: orig.borrow,
});
#[allow(clippy::mem_forget)]
mem::forget(orig);
res
}
None => Err(orig),
}
}
}
impl<T: Display + ?Sized> Display for ARef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ARef::deref(self).fmt(f)
}
}
impl<T: Hash + ?Sized> Hash for ARef<'_, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
ARef::deref(self).hash(state)
}
}
impl<A: PartialEq<B> + ?Sized, B: ?Sized> PartialEq<ARef<'_, B>> for ARef<'_, A> {
fn eq(&self, other: &ARef<'_, B>) -> bool {
ARef::deref(self).eq(ARef::deref(other))
}
}
impl<A: Eq + ?Sized> Eq for ARef<'_, A> {}
impl<A: PartialOrd<B> + ?Sized, B: ?Sized> PartialOrd<ARef<'_, B>> for ARef<'_, A> {
fn partial_cmp(&self, other: &ARef<'_, B>) -> Option<Ordering> {
ARef::deref(self).partial_cmp(ARef::deref(other))
}
}
impl<A: Ord + ?Sized> Ord for ARef<'_, A> {
fn cmp(&self, other: &Self) -> Ordering {
ARef::deref(self).cmp(ARef::deref(other))
}
}
pub trait AsARef<T: ?Sized> {
fn as_aref(this: &Self) -> ARef<T>;
fn try_as_aref(this: &Self) -> Result<ARef<T>, BorrowError>;
fn as_ref_cell(this: &Self) -> Option<&RefCell<T>>;
}
impl<T: ?Sized> AsARef<T> for T {
fn as_aref(this: &Self) -> ARef<T> {
ARef::new_ptr(this)
}
fn try_as_aref(this: &Self) -> Result<ARef<T>, BorrowError> {
Ok(ARef::new_ptr(this))
}
fn as_ref_cell(_this: &Self) -> Option<&RefCell<T>> {
None
}
}
impl<T: ?Sized> AsARef<T> for RefCell<T> {
fn as_aref(this: &Self) -> ARef<T> {
ARef::new_ref(this.borrow())
}
fn try_as_aref(this: &Self) -> Result<ARef<T>, BorrowError> {
Ok(ARef::new_ref(this.try_borrow()?))
}
fn as_ref_cell(this: &Self) -> Option<&RefCell<T>> {
Some(this)
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use std::mem;
use super::*;
use crate::cast;
#[test]
fn test_from_ref_docs() {
let c = RefCell::new((5, 'b'));
let b1: ARef<(u32, char)> = ARef::new_ref(c.borrow());
let b2: ARef<u32> = ARef::map(b1, |t| &t.0);
assert_eq!(*b2, 5);
let cell = RefCell::new([1, 2, 3, 4]);
let borrow = ARef::new_ref(cell.borrow());
let (begin, end) = ARef::map_split(borrow, |slice| slice.split_at(2));
assert_eq!(*begin, [1, 2]);
assert_eq!(*end, [3, 4]);
}
#[test]
fn test_borrow_guards() {
let c = RefCell::new(5);
assert!(c.try_borrow_mut().is_ok());
let r1 = ARef::new_ref(c.borrow());
assert!(c.try_borrow_mut().is_err());
let r2 = c.borrow();
assert!(c.try_borrow_mut().is_err());
mem::drop(r1);
assert!(c.try_borrow_mut().is_err());
mem::drop(r2);
assert!(c.try_borrow_mut().is_ok());
}
#[test]
fn test_pointer_basics() {
let c = "test".to_owned();
let p = ARef::new_ptr(&c);
let p2 = ARef::map(p, |x| &x[1..3]);
assert_eq!(&*p2, "es");
}
#[test]
fn test_ref_map_dropping() {
let c = RefCell::new("test".to_owned());
let p = ARef::new_ref(c.borrow());
let p = ARef::map(p, |x| &x[1..3]);
assert_eq!(&*p, "es");
mem::drop(p);
assert!(c.try_borrow_mut().is_ok());
}
#[test]
fn test_ref_filter_map_dropping() {
let c = RefCell::new("test".to_owned());
let p = ARef::new_ref(c.borrow());
let p = ARef::filter_map(p, |x| Some(&x[1..3])).unwrap();
assert_eq!(&*p, "es");
mem::drop(p);
assert!(c.try_borrow_mut().is_ok());
}
#[test]
fn test_ref_as_expected() {
let orig = RefCell::new("test".to_owned());
let p = orig.borrow();
let p2 = Ref::clone(&p);
let (pointer, cell): (usize, usize) = unsafe { mem::transmute(p) };
assert_eq!(pointer, cast::ptr_to_usize(Ref::deref(&p2)));
assert_ne!(cell, 0);
let _ignore: Ref<String> = unsafe { mem::transmute((pointer, cell)) };
}
#[test]
fn test_as_aref() {
fn get_str(x: &impl AsARef<String>) -> ARef<str> {
ARef::map(AsARef::as_aref(x), |x| x.as_str())
}
let a = RefCell::new("hello".to_owned());
let b = "world".to_owned();
assert_eq!(&*get_str(&a), "hello");
assert_eq!(&*get_str(&b), "world");
}
}