* map signals mutably * boxed signals * remove mutable borrow type from boxed writable * improve map mut example * restore boxed_mut helper * make boxed readable/writable types copy * Move away from the ReadOnlySignal alias in examples * automatically convert Write * Fix some tests * ReadSignal and WriteSignal * move read only signal alias * make maps copy * store prototype * implement derive macro * Handle generics in the derive macro * Make Selector boxable * derive boxed conversion * Get rid of store type * Remove hardcoded type from derive macro * selector -> store * Refactor store implementation * more vec methods * todo mvc store example * Implement Storable for HashMap * fix write * restore sorting logic in todomvc * derive readable and writable * improve todomvc store example * Result store * Option store * collect dead subscribers * fix clippy and formatting * recognize some common foreign types automatically * fix foreign stores in props * slice and array selectors * clean up store example * writable is child owned * box selector * read and write to builtins * re-use the proc macro in the derive macro * Switch to a extension trait based approach * switch to extension traits in the derive macro * transposed method * derive enums * Expose Store::new * remove stores from the prelude * More consistent naming scheme for SelectorScope methods * document hashmap methods * document index extension * document option ext * document result ext * document slice extension * document the vec extension * make more methods borrow the store mutably * Move to impls to have higher priority over the builtin write extension traits * fix deadlock * finish stores documentation * Document the store macro * cleanup docs and organization * finish derive docs * fix typo * fix clippy * fix doc tests * fix derive macro with generics * fix clippy * test and fix empty structs * clean up todomvc store example * default to a boxed store writer to make it easier to type when providing to the context api * fix cargo doc * fix clippy in store example * relax generational box lifetime bounds * relax bounds * relax signal bounds * fix compilation * read store * relax bounds on default store impls * fix enum derive * Convert to readsignal and writesignal automatically * simplify type matching logic * add write store * unsized stores * shrink the path vec type * use more concrete types * remove opaque types * remove copy bounds * use lens as generic name everywhere * more consistent borrow semantics * use a concrete type for the signal metadata and mention references in the map docs * improve todomvc store docs * document store derive macro internals * refactor macro * improve selector node documentation * fix clippy * add btreemap * fix doc tests * more option methods * fix doc * fix clippy * add a bunch more result methods * add a bunch of bounds to the iterators * fix clippy * remove option from subscribers * add store macro to generate extension traits * fix store example * fix deadlock * fix docs * fix doc tests * fix clippy * add stores to public api * add store macro, and then update todomvc_store example --------- Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
231 lines
6.7 KiB
Rust
231 lines
6.7 KiB
Rust
use generational_box::{GenerationalBox, Storage, SyncStorage, UnsyncStorage};
|
|
|
|
/// # Example
|
|
///
|
|
/// ```compile_fail
|
|
/// let data = String::from("hello world");
|
|
/// let owner = UnsyncStorage::owner();
|
|
/// let key = owner.insert(&data);
|
|
/// drop(data);
|
|
/// assert_eq!(*key.read(), "hello world");
|
|
/// ```
|
|
#[allow(unused)]
|
|
fn compile_fail() {}
|
|
|
|
#[test]
|
|
fn leaking_is_ok() {
|
|
fn leaking_is_ok_test<S: Storage<String> + 'static>() {
|
|
let data = String::from("hello world");
|
|
let key;
|
|
{
|
|
// create an owner
|
|
let owner = S::owner();
|
|
// insert data into the store
|
|
key = owner.insert(data);
|
|
// don't drop the owner
|
|
std::mem::forget(owner);
|
|
}
|
|
assert_eq!(
|
|
key.try_read().as_deref().unwrap(),
|
|
&"hello world".to_string()
|
|
);
|
|
}
|
|
|
|
leaking_is_ok_test::<UnsyncStorage>();
|
|
leaking_is_ok_test::<SyncStorage>();
|
|
}
|
|
|
|
#[test]
|
|
fn drops() {
|
|
fn drops_test<S: Storage<String> + 'static>() {
|
|
let data = String::from("hello world");
|
|
let key;
|
|
{
|
|
// create an owner
|
|
let owner = S::owner();
|
|
// insert data into the store
|
|
key = owner.insert(data);
|
|
// drop the owner
|
|
}
|
|
assert!(key.try_read().is_err());
|
|
}
|
|
|
|
drops_test::<UnsyncStorage>();
|
|
drops_test::<SyncStorage>();
|
|
}
|
|
|
|
#[test]
|
|
fn works() {
|
|
fn works_test<S: Storage<i32> + 'static>() {
|
|
let owner = S::owner();
|
|
let key = owner.insert(1);
|
|
|
|
assert_eq!(*key.read(), 1);
|
|
}
|
|
|
|
works_test::<UnsyncStorage>();
|
|
works_test::<SyncStorage>();
|
|
}
|
|
|
|
#[test]
|
|
fn insert_while_reading() {
|
|
fn insert_while_reading_test<S: Storage<String> + Storage<&'static i32> + 'static>() {
|
|
let owner = S::owner();
|
|
let key;
|
|
{
|
|
let data: String = "hello world".to_string();
|
|
key = owner.insert(data);
|
|
}
|
|
let value = key.read();
|
|
owner.insert(&1);
|
|
assert_eq!(*value, "hello world");
|
|
}
|
|
|
|
insert_while_reading_test::<UnsyncStorage>();
|
|
insert_while_reading_test::<SyncStorage>();
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn panics() {
|
|
fn panics_test<S: Storage<i32> + 'static>() {
|
|
let owner = S::owner();
|
|
|
|
let key = owner.insert(1);
|
|
drop(owner);
|
|
|
|
assert_eq!(*key.read(), 1);
|
|
}
|
|
|
|
panics_test::<UnsyncStorage>();
|
|
panics_test::<SyncStorage>();
|
|
}
|
|
|
|
#[test]
|
|
fn fuzz() {
|
|
fn maybe_owner_scope<S: Storage<String> + 'static>(
|
|
valid_keys: &mut Vec<GenerationalBox<String, S>>,
|
|
invalid_keys: &mut Vec<GenerationalBox<String, S>>,
|
|
path: &mut Vec<u8>,
|
|
) {
|
|
let branch_cutoff = 5;
|
|
let children = if path.len() < branch_cutoff {
|
|
rand::random::<u8>() % 4
|
|
} else {
|
|
rand::random::<u8>() % 2
|
|
};
|
|
|
|
for i in 0..children {
|
|
let owner = S::owner();
|
|
let value = format!("hello world {path:?}");
|
|
let key = owner.insert(value.clone());
|
|
println!("created new box {key:?}");
|
|
valid_keys.push(key);
|
|
path.push(i);
|
|
// read all keys
|
|
println!("{:?}", path);
|
|
for key in valid_keys.iter() {
|
|
println!("reading {key:?}");
|
|
let value = key.read();
|
|
println!("{:?}", &*value);
|
|
assert!(value.starts_with("hello world"));
|
|
}
|
|
for key in invalid_keys.iter() {
|
|
println!("reading invalid {key:?}");
|
|
assert!(key.try_read().is_err());
|
|
}
|
|
maybe_owner_scope(valid_keys, invalid_keys, path);
|
|
|
|
// After all the children run, we should still have our data
|
|
let key_value = &*key.read();
|
|
println!("{:?}", key_value);
|
|
assert_eq!(key_value, &value);
|
|
|
|
let invalid = valid_keys.pop().unwrap();
|
|
println!("popping {invalid:?}");
|
|
invalid_keys.push(invalid);
|
|
path.pop();
|
|
}
|
|
}
|
|
|
|
for _ in 0..10 {
|
|
maybe_owner_scope::<UnsyncStorage>(&mut Vec::new(), &mut Vec::new(), &mut Vec::new());
|
|
}
|
|
|
|
for _ in 0..10 {
|
|
maybe_owner_scope::<SyncStorage>(&mut Vec::new(), &mut Vec::new(), &mut Vec::new());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn fuzz_rc() {
|
|
fn maybe_owner_scope<S: Storage<String>>(
|
|
valid_keys: &mut Vec<Vec<GenerationalBox<String, S>>>,
|
|
invalid_keys: &mut Vec<GenerationalBox<String, S>>,
|
|
path: &mut Vec<u8>,
|
|
) {
|
|
let branch_cutoff = 5;
|
|
let children = if path.len() < branch_cutoff {
|
|
rand::random::<u8>() % 4
|
|
} else {
|
|
rand::random::<u8>() % 2
|
|
};
|
|
|
|
for i in 0..children {
|
|
let owner = S::owner();
|
|
let value = format!("hello world {path:?}");
|
|
let key = owner.insert_rc(value.clone());
|
|
println!("created new box {key:?}");
|
|
let mut keys = vec![key];
|
|
for _ in 0..rand::random::<u8>() % 10 {
|
|
if rand::random::<u8>() % 2 == 0 {
|
|
let owner = S::owner();
|
|
let key = owner.insert_reference(key).unwrap();
|
|
println!("created new reference {key:?}");
|
|
invalid_keys.push(key);
|
|
}
|
|
let key = owner.insert_reference(key).unwrap();
|
|
println!("created new reference {key:?}");
|
|
keys.push(key);
|
|
}
|
|
valid_keys.push(keys.clone());
|
|
path.push(i);
|
|
// read all keys
|
|
println!("{:?}", path);
|
|
for keys in valid_keys.iter() {
|
|
for key in keys {
|
|
println!("reading {key:?}");
|
|
let value = key.read();
|
|
println!("{:?}", &*value);
|
|
assert!(value.starts_with("hello world"));
|
|
}
|
|
}
|
|
for key in invalid_keys.iter() {
|
|
println!("reading invalid {key:?}");
|
|
assert!(key.try_read().is_err());
|
|
}
|
|
maybe_owner_scope(valid_keys, invalid_keys, path);
|
|
|
|
// After all the children run, we should still have our data
|
|
for key in keys {
|
|
let key_value = &*key.read();
|
|
println!("{:?}", key_value);
|
|
assert_eq!(key_value, &value);
|
|
}
|
|
|
|
let invalid = valid_keys.pop().unwrap();
|
|
println!("popping {invalid:?}");
|
|
invalid_keys.extend(invalid);
|
|
path.pop();
|
|
}
|
|
}
|
|
|
|
for _ in 0..10 {
|
|
maybe_owner_scope::<UnsyncStorage>(&mut Vec::new(), &mut Vec::new(), &mut Vec::new());
|
|
}
|
|
|
|
for _ in 0..10 {
|
|
maybe_owner_scope::<SyncStorage>(&mut Vec::new(), &mut Vec::new(), &mut Vec::new());
|
|
}
|
|
}
|