#[cfg(doc)]
use core::cell::RefCell;
use core::{
cell::{Ref, RefMut},
ops::{Add, Deref},
ptr::addr_of,
};
use generic_array::{
arr,
sequence::Lengthen,
typenum::{Add1, B1, U0},
ArrayLength, GenericArray,
};
use crate::{clone_arr::clone_arr, NestedRefMut};
pub struct NestedRef<'a, T, N>
where
T: ?Sized,
N: ArrayLength<Ref<'a, ()>>,
{
inner: Ref<'a, T>,
outer: GenericArray<Ref<'a, ()>, N>,
}
impl<'a, T> NestedRef<'a, T, U0>
where
T: ?Sized,
{
#[must_use]
#[inline]
pub fn new(inner: Ref<'a, T>) -> Self {
let outer = arr![Ref<'a, ()>;];
Self { inner, outer }
}
}
impl<'a, T, N> NestedRef<'a, T, N>
where
T: ?Sized,
N: ArrayLength<Ref<'a, ()>>,
{
#[must_use]
#[inline]
#[allow(clippy::should_implement_trait)]
pub fn clone(orig: &Self) -> Self {
Self {
inner: Ref::clone(&orig.inner),
outer: clone_arr(&orig.outer),
}
}
#[inline]
pub fn map<U, F>(orig: Self, f: F) -> NestedRef<'a, U, N>
where
U: ?Sized,
F: FnOnce(&T) -> &U,
{
NestedRef {
inner: Ref::map(orig.inner, f),
outer: orig.outer,
}
}
#[inline]
#[allow(clippy::missing_errors_doc)]
pub fn filter_map<U, F>(orig: Self, f: F) -> Result<NestedRef<'a, U, N>, Self>
where
U: ?Sized,
F: FnOnce(&T) -> Option<&U>,
{
let outer = orig.outer;
match Ref::filter_map(orig.inner, f) {
Ok(inner) => Ok(NestedRef { inner, outer }),
Err(inner) => Err(NestedRef { inner, outer }),
}
}
#[inline]
pub fn map_split<U, V, F>(orig: Self, f: F) -> (NestedRef<'a, U, N>, NestedRef<'a, V, N>)
where
U: ?Sized,
V: ?Sized,
F: FnOnce(&T) -> (&U, &V),
{
let outer_a = clone_arr(&orig.outer);
let outer_b = orig.outer;
let (inner_a, inner_b) = Ref::map_split(orig.inner, f);
(
NestedRef {
inner: inner_a,
outer: outer_a,
},
NestedRef {
inner: inner_b,
outer: outer_b,
},
)
}
}
impl<'a, T, N> NestedRef<'a, T, N>
where
T: ?Sized,
N: ArrayLength<Ref<'a, ()>> + Add<B1>,
<N as Add<B1>>::Output: ArrayLength<Ref<'a, ()>>,
GenericArray<Ref<'a, ()>, N>:
Lengthen<Ref<'a, ()>, Longer = GenericArray<Ref<'a, ()>, Add1<N>>>,
{
#[inline]
pub fn map_ref<U, F>(orig: Self, f: F) -> NestedRef<'a, U, Add1<N>>
where
U: ?Sized,
F: FnOnce(&T) -> Ref<'_, U>,
{
let (t, outer) = unsafe { orig.nest() };
let inner = f(t);
NestedRef { inner, outer }
}
#[inline]
pub fn map_ref_mut<U, F>(orig: Self, f: F) -> NestedRefMut<'a, U, Add1<N>>
where
U: ?Sized,
F: FnOnce(&T) -> RefMut<'_, U>,
{
let (t, outer) = unsafe { orig.nest() };
let inner = f(t);
NestedRefMut { inner, outer }
}
#[must_use]
#[inline]
unsafe fn nest(self) -> (&'a T, GenericArray<Ref<'a, ()>, Add1<N>>) {
let t = addr_of!(*self.inner);
let t: &'a T = unsafe { &*t };
let new = Ref::map(self.inner, |_| &());
let outer = self.outer.append(new);
(t, outer)
}
}
impl<'a, T, N> Deref for NestedRef<'a, T, N>
where
T: ?Sized,
N: ArrayLength<Ref<'a, ()>>,
{
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[cfg(test)]
mod tests {
use core::cell::{Cell, RefCell};
use crate::NestedRef;
#[test]
fn simple() {
let rc = RefCell::new(Cell::new(0));
let nr = NestedRef::new(rc.borrow());
assert_eq!(nr.get(), 0);
assert_eq!(rc.borrow().get(), 0);
nr.set(1);
assert_eq!(nr.get(), 1);
assert_eq!(rc.borrow().get(), 1);
rc.borrow().set(2);
assert_eq!(nr.get(), 2);
assert_eq!(rc.borrow().get(), 2);
}
#[test]
fn deep() {
let rc = RefCell::new(RefCell::new(RefCell::new(Cell::new(0))));
let nr = NestedRef::new(rc.borrow());
let nr = NestedRef::map_ref(nr, RefCell::borrow);
let nr = NestedRef::map_ref(nr, RefCell::borrow);
assert_eq!(nr.get(), 0);
assert_eq!(rc.borrow().borrow().borrow().get(), 0);
nr.set(1);
assert_eq!(nr.get(), 1);
assert_eq!(rc.borrow().borrow().borrow().get(), 1);
rc.borrow().borrow().borrow().set(2);
assert_eq!(nr.get(), 2);
assert_eq!(rc.borrow().borrow().borrow().get(), 2);
}
#[test]
fn clone() {
let rc = RefCell::new(Cell::new(0));
let nr = NestedRef::new(rc.borrow());
assert_eq!(nr.get(), 0);
assert_eq!(NestedRef::clone(&nr).get(), 0);
nr.set(1);
assert_eq!(nr.get(), 1);
assert_eq!(NestedRef::clone(&nr).get(), 1);
NestedRef::clone(&nr).set(2);
assert_eq!(nr.get(), 2);
assert_eq!(NestedRef::clone(&nr).get(), 2);
}
#[test]
fn map() {
let rc = RefCell::new((Cell::new(0), Cell::new(0)));
let nr = NestedRef::new(rc.borrow());
let nr = NestedRef::map(nr, |x| &x.0);
assert_eq!(nr.get(), 0);
assert_eq!(rc.borrow().0.get(), 0);
nr.set(1);
assert_eq!(nr.get(), 1);
assert_eq!(rc.borrow().0.get(), 1);
rc.borrow().0.set(2);
assert_eq!(nr.get(), 2);
assert_eq!(rc.borrow().0.get(), 2);
}
#[test]
fn filter_map() {
let rc = RefCell::new(Cell::new(0));
let nr = NestedRef::new(rc.borrow());
let nr = NestedRef::filter_map::<(), _>(nr, |_| None)
.map(|_| ())
.expect_err("This filter_map should fail");
let nr = NestedRef::filter_map(nr, |x| Some(x))
.map_err(|_| ())
.expect("This filter_map should succeed");
assert_eq!(nr.get(), 0);
assert_eq!(rc.borrow().get(), 0);
nr.set(1);
assert_eq!(nr.get(), 1);
assert_eq!(rc.borrow().get(), 1);
rc.borrow().set(2);
assert_eq!(nr.get(), 2);
assert_eq!(rc.borrow().get(), 2);
}
#[test]
fn map_split() {
let rc = RefCell::new((Cell::new(0), Cell::new(0)));
let nr = NestedRef::new(rc.borrow());
let (nr, _nr2) = NestedRef::map_split(nr, |x| (&x.0, &x.1));
assert_eq!(nr.get(), 0);
assert_eq!(rc.borrow().0.get(), 0);
nr.set(1);
assert_eq!(nr.get(), 1);
assert_eq!(rc.borrow().0.get(), 1);
rc.borrow().0.set(2);
assert_eq!(nr.get(), 2);
assert_eq!(rc.borrow().0.get(), 2);
}
}