use core::{cell::Cell, mem::MaybeUninit, ptr};
use std::{format, rc::Rc, string::String};
use crate::{CellLike, CursorMut, RecCell, RecToken, SliceCursor, SliceCursorMut};
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
#[test]
fn trait_impls_and_owned_cell_operations() {
assert_send::<RecCell<'static, usize>>();
assert_sync::<RecCell<'static, usize>>();
let mut from = RecCell::from(String::from("from"));
assert_eq!(from.as_mut(), "from");
from.as_mut().make_ascii_uppercase();
assert_eq!(from.into_inner(), "FROM");
assert_eq!(RecCell::<usize>::default().into_inner(), 0);
RecToken::new(|mut token| {
let mut cell = RecCell::new(1usize);
assert_eq!(format!("{cell:?}"), "RecCell { .. }");
assert_eq!(*cell.borrow(&token), 1);
*cell.borrow_mut(&mut token) = 2;
*cell.get_mut() = 3;
assert_eq!(cell.into_inner(), 3);
assert_eq!(format!("{token:?}"), "RecToken { .. }");
});
}
#[test]
fn debug_impls_do_not_expose_or_require_the_inner_value() {
struct NotDebug;
RecToken::new(|mut token| {
let cell = RecCell::new(NotDebug);
assert_eq!(
format!("{:?}", cell.cursor(&token)),
"Cursor { cell: RecCell { .. }, token: RecToken { .. } }"
);
assert_eq!(
format!("{:?}", cell.cursor_mut(&mut token)),
"CursorMut { cell: RecCell { .. }, token: RecToken { .. } }"
);
let cells = [RecCell::new(NotDebug)];
assert_eq!(
format!("{:?}", SliceCursor::new(&cells, &token)),
"SliceCursor { cells: [RecCell { .. }], token: RecToken { .. } }"
);
assert_eq!(
format!("{:?}", SliceCursorMut::new(&cells, &mut token)),
"SliceCursorMut { cells: [RecCell { .. }], token: RecToken { .. } }"
);
});
RecToken::new(|token| {
let (debug, _) = token
.with_builder(|builder| Ok::<_, ()>((format!("{builder:?}"), builder)))
.unwrap();
assert_eq!(debug, "RecBuilder { .. }");
});
}
#[test]
fn reinterpreted_scalar_slice_and_array_storage_round_trip() {
let mut scalar = 1usize;
let scalar_ptr = ptr::from_mut(&mut scalar);
let scalar_cell = RecCell::from_mut(&mut scalar);
assert_eq!(scalar_cell.as_ptr(), scalar_ptr);
*scalar_cell.get_mut() = 2;
assert_eq!(scalar, 2);
let mut values = [1usize, 2, 3];
let values_ptr = values.as_mut_ptr();
RecToken::new(|mut token| {
let cells = RecCell::from_slice_mut(&mut values);
assert_eq!(RecCell::as_slice_ptr(cells).cast::<usize>(), values_ptr);
assert_eq!(RecCell::borrow_slice(cells, &token), &[1, 2, 3]);
RecCell::borrow_slice_mut(cells, &mut token)[1] = 20;
RecCell::get_slice_mut(cells)[2] = 30;
assert_eq!(RecCell::borrow_slice(cells, &token), &[1, 20, 30]);
let mut array = RecCell::new([4usize, 5, 6]);
let elements = array.as_array_of_cells_mut();
let cells = RecCell::from_array_of_cells_mut(elements).as_array_of_cells_mut();
*cells[0].borrow_mut(&mut token) = 40;
assert_eq!(RecCell::borrow_slice(cells, &token), &[40, 5, 6]);
assert_eq!(
RecCell::from_array_of_cells(cells).borrow(&token),
&[40, 5, 6]
);
});
assert_eq!(values, [1, 20, 30]);
}
#[test]
fn shared_cursors_cover_mapping_updates_errors_and_copy() {
RecToken::new(|token| {
let tail = RecCell::new(2usize);
let head = RecCell::new(&tail);
let other = RecCell::new(3usize);
let cursor = head.cursor(&token);
assert_eq!(*cursor.get().borrow(&token), 2);
let cursor = cursor.map(|next| *next);
assert_eq!(cursor.get(), &2);
let cursor = cursor.map_cell(|_| &other);
assert_eq!(cursor.get(), &3);
let err = cursor.try_map::<RecCell<'_, usize>, _>(|_| Err("map failed"));
assert_eq!(err.unwrap_err(), "map failed");
let mut cursor = head.cursor(&token).map(|next| *next);
cursor.update(|_| &other);
assert_eq!(cursor.get(), &3);
assert_eq!(
cursor.try_update(|_| Err::<&RecCell<'_, usize>, _>("update failed")),
Err("update failed")
);
assert_eq!(cursor.get(), &3);
let cursor = cursor.try_map_cell(Ok::<_, ()>).unwrap();
let (cell, token) = cursor.into_inner();
assert_eq!(cell.borrow(token), &3);
});
}
#[test]
fn mutable_cursors_cover_mapping_updates_replacement_and_errors() {
RecToken::new(|mut token| {
let first = RecCell::new(1usize);
let next = RecCell::new(2usize);
let mut cursor = CursorMut::new(&first, &mut token);
assert_eq!(cursor.get(), &1);
*cursor.get_mut() = 10;
assert_eq!(cursor.replace(11), 10);
assert_eq!(cursor.take(), 11);
*cursor.get_mut() = 1;
cursor.update(|_| &next);
assert_eq!(cursor.get(), &2);
assert_eq!(
cursor.try_update(|_| Err::<&RecCell<'_, usize>, _>("update failed")),
Err("update failed")
);
assert_eq!(cursor.get(), &2);
let cursor = cursor.map_cell(|cell| cell);
let cursor = cursor
.try_map(|value| {
*value = 20;
Ok::<_, ()>(&first)
})
.unwrap();
assert_eq!(cursor.get(), &1);
let err = cursor.try_map_cell::<RecCell<'_, usize>, _>(|_| Err("map failed"));
assert_eq!(err.unwrap_err(), "map failed");
let (cell, token) = first.cursor_mut(&mut token).into_inner();
assert_eq!(cell.borrow(token), &1);
});
}
#[test]
fn slice_cursors_cover_every_shape_transition() {
RecToken::new(|mut token| {
let first = [RecCell::new(1usize), RecCell::new(2)];
let second = [RecCell::new(3usize), RecCell::new(4)];
let single = RecCell::new(5usize);
let cursor = SliceCursor::new(&first, &token);
assert_eq!(cursor.get(), &[1, 2]);
assert_eq!(cursor.clone().get(), &[1, 2]);
let cursor = cursor.map(|_| &single).map_cell(core::array::from_ref);
assert_eq!(cursor.get(), &[5]);
let err = cursor.try_map_cell::<RecCell<'_, usize>, _>(|_| Err("map failed"));
assert_eq!(err.unwrap_err(), "map failed");
{
let mut cursor = SliceCursor::new(&first, &token);
cursor.update(|_| &second);
assert_eq!(cursor.get(), &[3, 4]);
assert_eq!(
cursor.try_update(|_| Err::<&[RecCell<'_, usize>], _>("update failed")),
Err("update failed")
);
let (cells, token) = cursor.into_inner();
assert_eq!(RecCell::borrow_slice(cells, token), &[3, 4]);
}
let mut cursor = SliceCursorMut::new(&first, &mut token);
cursor.get_mut()[0] = 10;
let cursor = cursor.map(|values| {
values[1] = 20;
&single
});
assert_eq!(cursor.get(), &5);
let mut cursor = cursor.map_cell(core::array::from_ref);
cursor.get_mut()[0] = 50;
let err = cursor.try_map::<RecCell<'_, usize>, _>(|_| Err("map failed"));
assert_eq!(err.unwrap_err(), "map failed");
let mut cursor = SliceCursorMut::new(&first, &mut token);
cursor.update(|_| &second);
assert_eq!(cursor.get(), &[3, 4]);
assert_eq!(
cursor.try_update(|_| Err::<&[RecCell<'_, usize>], _>("update failed")),
Err("update failed")
);
let (cells, token) = cursor.into_inner();
assert_eq!(RecCell::borrow_slice(cells, token), &[3, 4]);
let array_cursor = first.as_cursor(token);
assert_eq!(array_cursor.get(), &[10, 20]);
});
}
#[test]
fn cyclic_construction_preserves_identity_and_initializes_all_container_forms() {
struct Node<'a, 't> {
next: &'a RecCell<'t, Node<'a, 't>>,
value: usize,
}
RecToken::new(|token| {
let mut self_slot = MaybeUninit::<Node<'_, '_>>::uninit();
let (self_cell, token) = RecCell::new_cyclic(&mut self_slot, token, |cell| {
(
cell,
Node {
next: cell,
value: 1,
},
)
});
let node = self_cell.borrow(&token);
assert_eq!(node.value, 1);
assert!(ptr::eq(node.next, self_cell));
let mut pair = (MaybeUninit::uninit(), MaybeUninit::uninit());
let ((left, right), token) = token
.make_cyclic((&mut pair.0, &mut pair.1), |(left, right)| {
((left, right), (2usize, 3usize))
});
assert_eq!((*left.borrow(&token), *right.borrow(&token)), (2, 3));
let mut slots = [const { MaybeUninit::<usize>::uninit() }; 3];
let [first, second, third] = &mut slots;
let ((), token) = token.make_cyclic([first, second, third], |cells| {
(
(),
[
cells[0].as_ptr() as usize,
cells[1].as_ptr() as usize,
cells[2].as_ptr() as usize,
],
)
});
for (cell, slot) in slots.iter().enumerate() {
let value = unsafe { slot.assume_init_ref() };
assert_eq!(value, &(slots[cell].as_ptr() as usize));
}
assert_eq!(RecCell::new(9).borrow(&token), &9);
});
}
#[test]
fn cyclic_builder_supports_empty_and_maximum_tuple_inputs() {
RecToken::new(|token| {
let ((), token) = token.make_cyclic((), |()| ((), ()));
let mut slots = [const { MaybeUninit::<usize>::uninit() }; 12];
let [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11] = &mut slots;
let ((), token) = token
.make_cyclic((s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11), |_| {
((), (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))
});
for (index, slot) in slots.iter().enumerate() {
assert_eq!(unsafe { slot.assume_init_ref() }, &index);
}
assert_eq!(RecCell::new(1).borrow(&token), &1);
});
}
#[test]
fn fallible_builders_do_not_write_and_successful_builder_sequences_do() {
RecToken::new(|token| {
let mut untouched = MaybeUninit::new(7usize);
let error =
RecCell::try_new_cyclic(&mut untouched, token, |_| Err::<((), usize), _>("failed"));
assert_eq!(error, Err("failed"));
assert_eq!(unsafe { untouched.assume_init_ref() }, &7);
});
RecToken::new(|token| {
let mut direct = MaybeUninit::uninit();
let (value, token) = token
.try_make_cyclic(&mut direct, |_| Ok::<_, ()>((5usize, 6usize)))
.unwrap();
assert_eq!(value, 5);
assert_eq!(unsafe { direct.assume_init_ref() }, &6);
let mut first = MaybeUninit::uninit();
let mut second = MaybeUninit::uninit();
let ((first_cell, second_cell), token) = token
.with_builder(|builder| {
builder.try_add(&mut first, |builder, first_cell| {
let (second_cell, builder) = builder
.try_add(&mut second, |builder, second_cell| {
Ok::<_, ()>((second_cell, builder, 20usize))
})?;
Ok::<_, ()>(((first_cell, second_cell), builder, 10usize))
})
})
.unwrap();
assert_eq!(
(*first_cell.borrow(&token), *second_cell.borrow(&token)),
(10, 20)
);
let mut failing = MaybeUninit::new(30usize);
let error = token.with_builder(|builder| {
builder.try_add(&mut failing, |_, _| {
Err::<((), crate::RecBuilder<'_, '_>, usize), _>("failed")
})
});
assert_eq!(error, Err("failed"));
assert_eq!(unsafe { failing.assume_init_ref() }, &30);
});
}
struct DropCounter(Rc<Cell<usize>>);
impl Drop for DropCounter {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
}
}
#[test]
fn owned_cells_drop_once_and_cyclic_storage_requires_explicit_drop() {
let owned_drops = Rc::new(Cell::new(0));
{
let _cell = RecCell::new(DropCounter(owned_drops.clone()));
}
assert_eq!(owned_drops.get(), 1);
let cyclic_drops = Rc::new(Cell::new(0));
RecToken::new(|token| {
let mut slot = MaybeUninit::uninit();
let ((), token) = RecCell::new_cyclic(&mut slot, token, |_| {
((), DropCounter(cyclic_drops.clone()))
});
assert_eq!(cyclic_drops.get(), 0);
unsafe { slot.assume_init_drop() };
assert_eq!(cyclic_drops.get(), 1);
assert_eq!(RecCell::new(1).borrow(&token), &1);
});
}
#[cfg(feature = "cyclic_with_mut")]
#[test]
fn mutable_token_cyclic_constructors_restore_the_token() {
RecToken::new(|mut token| {
let mut first = MaybeUninit::uninit();
let value = token.make_cyclic_with_mut(&mut first, |_| (7usize, 11usize));
assert_eq!(value, 7);
assert_eq!(unsafe { first.assume_init_ref() }, &11);
let mut second = MaybeUninit::uninit();
let value = RecCell::new_cyclic_with_mut(&mut second, &mut token, |_| (8usize, 12usize));
assert_eq!(value, 8);
assert_eq!(unsafe { second.assume_init_ref() }, &12);
assert_eq!(RecCell::new(1).borrow(&token), &1);
});
}