use alloc::string::String;
use core::num::NonZeroU32;
#[cfg(feature = "secondary-indexes")]
use core::num::NonZeroU64;
#[cfg(feature = "secondary-indexes")]
extern crate std;
#[cfg(feature = "secondary-indexes")]
use self::std::panic::{AssertUnwindSafe, catch_unwind};
use super::{
Capacity, IndexKey, WarpId, WarpTable, WarpTableGetOutcome, WarpTableInsertOutcome,
WarpTableInsertWithIdOutcome, WarpTableRow, integer_index_word,
};
#[cfg(feature = "secondary-indexes")]
use super::{
MutableSecondaryIndexLookupOutcome, MutableSelectionForEachOutcome,
SecondaryIndexLookupOutcome, WarpTableRemoveOutcome, WarpTableReplaceOutcome,
WarpTableRowRelocation, WarpTableUpdateOutcome,
};
crate::value_wrapper! {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct PetId(u64);
}
#[derive(Debug, Eq, WarpTableRow, PartialEq)]
struct Pet {
name: String,
age: u8,
}
#[cfg(feature = "secondary-indexes")]
#[derive(Debug, Eq, WarpTableRow, PartialEq)]
struct IndexedPet {
name: String,
#[index(secondary, range)]
age: u8,
#[index(secondary)]
species: u8,
}
#[cfg(feature = "secondary-indexes")]
#[test]
fn guarded_updates_mutate_columns_and_move_index_memberships() -> Result<(), &'static str> {
let capacity = Capacity::new(NonZeroU32::new(1).ok_or("capacity was zero")?);
let mut pets = WarpTable::<IndexedPet>::try_new(capacity)
.map_err(|_| "indexed pet storage was unavailable")?;
let WarpTableInsertOutcome::Inserted { key, .. } = pets.insert(IndexedPet {
name: String::from("Luna"),
age: 4,
species: 2,
}) else {
return Err("Luna could not be inserted");
};
assert_eq!(
pets.update(key, |mut pet| {
assert_eq!(*pet.id, key);
pet.name_mut().push_str(" II");
(pet.set_age(7), *pet.species())
}),
WarpTableUpdateOutcome::Updated {
key,
row: 0,
output: (4, 2),
}
);
assert!(pets.query().find_age(4).is_empty());
assert_eq!(pets.query().find_age(7).len(), 1);
assert_eq!(
pets.query()
.find_age_in(7..=7)
.map_err(|_| "updated age range was unavailable")?
.len(),
1
);
let WarpTableGetOutcome::Present { row } = pets.get(key) else {
return Err("updated pet was unavailable");
};
assert_eq!((row.name.as_str(), *row.age), ("Luna II", 7));
assert_eq!(
pets.update(WarpId::new(NonZeroU64::MAX), |_| ()),
WarpTableUpdateOutcome::Absent {
key: WarpId::new(NonZeroU64::MAX),
}
);
Ok(())
}
#[cfg(feature = "secondary-indexes")]
#[test]
fn selected_updates_keep_every_membership_synchronized() -> Result<(), &'static str> {
let capacity = Capacity::new(NonZeroU32::new(3).ok_or("capacity was zero")?);
let mut pets = WarpTable::<IndexedPet>::try_new(capacity)
.map_err(|_| "indexed pet storage was unavailable")?;
for (name, age, species) in [("Luna", 4, 2), ("Milo", 4, 3), ("Suki", 7, 2)] {
let WarpTableInsertOutcome::Inserted { .. } = pets.insert(IndexedPet {
name: String::from(name),
age,
species,
}) else {
return Err("pet could not be inserted");
};
}
let selected = pets.query_mut().find_age(4);
assert_eq!(selected.len(), 2);
assert_eq!(
selected.for_each(|mut pet| {
pet.name_mut().push_str(" II");
assert_eq!(pet.set_age(9), 4);
}),
MutableSelectionForEachOutcome::Updated { rows: 2 }
);
assert!(pets.query().find_age(4).is_empty());
assert_eq!(pets.query().find_age(9).len(), 2);
assert_eq!(pets.query().find_species(2).len(), 2);
assert_eq!(
pets.query_mut()
.find_age_in(9..=9)
.map_err(|_| "updated age range was unavailable")?
.for_each(|mut pet| {
let _ = pet.set_species(7);
}),
MutableSelectionForEachOutcome::Updated { rows: 2 }
);
assert_eq!(pets.query().find_species(2).len(), 1);
assert_eq!(pets.query().find_species(3).len(), 0);
assert_eq!(pets.query().find_species(7).len(), 2);
let absent = pets.query_mut().find_age(99);
assert!(matches!(
absent,
MutableSecondaryIndexLookupOutcome::Absent {
secondary_key: 99,
..
}
));
assert_eq!(
pets.query_mut().find_age(99).for_each(|_| {}),
MutableSelectionForEachOutcome::Updated { rows: 0 }
);
Ok(())
}
#[cfg(feature = "secondary-indexes")]
#[test]
fn mutable_selection_algebra_composes_before_guarded_updates() -> Result<(), &'static str> {
let capacity = Capacity::new(NonZeroU32::new(3).ok_or("capacity was zero")?);
let mut pets = WarpTable::<IndexedPet>::try_new(capacity)
.map_err(|_| "indexed pet storage was unavailable")?;
for (name, age, species) in [("Luna", 4, 2), ("Milo", 4, 3), ("Suki", 7, 2)] {
let WarpTableInsertOutcome::Inserted { .. } = pets.insert(IndexedPet {
name: String::from(name),
age,
species,
}) else {
return Err("pet could not be inserted");
};
}
macro_rules! assert_algebra_len {
($left:ident($left_key:expr), $operation:ident, $right:ident($right_key:expr), $expected:expr) => {
assert_eq!(
pets.query_mut()
.$left($left_key)
.$operation(|pets| pets.$right($right_key))
.len(),
$expected
);
};
}
assert_algebra_len!(find_species(2), and, find_age(4), 1);
assert_algebra_len!(find_species(2), or, find_age(4), 3);
assert_algebra_len!(find_species(2), and_not, find_age(4), 1);
assert_algebra_len!(find_species(2), xor, find_age(4), 2);
assert_algebra_len!(find_age(99), or, find_species(2), 2);
assert_algebra_len!(find_species(2), or, find_age(99), 2);
assert_algebra_len!(find_age(99), and, find_species(2), 0);
assert_algebra_len!(find_species(2), and_not, find_age(99), 2);
assert_algebra_len!(find_age(99), and_not, find_species(2), 0);
assert_algebra_len!(find_age(99), xor, find_species(2), 2);
assert_algebra_len!(find_species(2), xor, find_age(99), 2);
macro_rules! assert_range_algebra_len {
($operation:ident, $expected:expr) => {
assert_eq!(
pets.query_mut()
.find_species(2)
.$operation(|pets| pets.find_age_in(4..=4))
.map_err(|_| "age range was unavailable")?
.len(),
$expected
);
};
}
assert_range_algebra_len!(or, 3);
assert_range_algebra_len!(and_not, 1);
assert_range_algebra_len!(xor, 2);
assert!(
pets.query_mut()
.find_age_in(50..=60)
.map_err(|_| "absent age range was unavailable")?
.is_empty()
);
let selected = pets
.query_mut()
.find_species(2)
.and(|pets| pets.find_age_in(4..=4))
.map_err(|_| "age range was unavailable")?;
assert_eq!(
selected.for_each(|mut pet| {
assert_eq!(pet.set_age(8), 4);
}),
MutableSelectionForEachOutcome::Updated { rows: 1 }
);
assert_eq!(pets.query().find_age(4).len(), 1);
assert_eq!(pets.query().find_age(8).len(), 1);
Ok(())
}
#[cfg(feature = "secondary-indexes")]
#[derive(Clone, Copy, Debug, Eq, WarpTableRow, PartialEq)]
struct IndexedEvent {
#[index(secondary, range)]
tick: u64,
#[index(secondary)]
kind: u8,
}
impl IndexKey for PetId {
fn index_word(self) -> u64 {
integer_index_word(self.value())
}
}
#[derive(Debug, Eq, WarpTableRow, PartialEq)]
#[index(primary = PetId)]
struct ImportedPet {
name: String,
}
#[test]
fn derived_warp_table_row_owns_columns_and_reconstructs_flat_borrows() -> Result<(), &'static str> {
let capacity = Capacity::new(NonZeroU32::new(2).ok_or("capacity was zero")?);
let mut pets =
WarpTable::<Pet>::try_new(capacity).map_err(|_| "pet storage was unavailable")?;
let WarpTableInsertOutcome::Inserted { key: luna_id, .. } = pets.insert(Pet {
name: String::from("Luna"),
age: 4,
}) else {
return Err("pet could not be inserted");
};
assert_eq!(luna_id, WarpId::<Pet>::MIN);
assert_eq!(pets.columns().name, [String::from("Luna")]);
let WarpTableGetOutcome::Present { row } = pets.get(luna_id) else {
return Err("inserted pet was unavailable");
};
assert_eq!((*row.id, row.name.as_str(), *row.age), (luna_id, "Luna", 4));
let mut imported = WarpTable::<ImportedPet>::try_new(capacity)
.map_err(|_| "imported pet storage was unavailable")?;
assert_eq!(
imported.insert_with_id(
PetId::new(42),
ImportedPet {
name: String::from("Milo"),
},
),
WarpTableInsertWithIdOutcome::Inserted {
key: PetId::new(42),
row: 0,
}
);
let WarpTableGetOutcome::Present { row } = imported.get(PetId::new(42)) else {
return Err("imported pet was unavailable");
};
assert_eq!((*row.id, row.name.as_str()), (PetId::new(42), "Milo"));
Ok(())
}
#[cfg(feature = "secondary-indexes")]
#[test]
fn secondary_queries_compose_and_resolve_flat_rows() -> Result<(), &'static str> {
let capacity = Capacity::new(NonZeroU32::new(3).ok_or("capacity was zero")?);
let mut pets = WarpTable::<IndexedPet>::try_new(capacity)
.map_err(|_| "indexed pet storage was unavailable")?;
let WarpTableInsertOutcome::Inserted { key: luna_id, .. } = pets.insert(IndexedPet {
name: String::from("Luna"),
age: 4,
species: 2,
}) else {
return Err("Luna could not be inserted");
};
let WarpTableInsertOutcome::Inserted { key: milo_id, .. } = pets.insert(IndexedPet {
name: String::from("Milo"),
age: 4,
species: 3,
}) else {
return Err("Milo could not be inserted");
};
let WarpTableInsertOutcome::Inserted { key: suki_id, .. } = pets.insert(IndexedPet {
name: String::from("Suki"),
age: 7,
species: 2,
}) else {
return Err("Suki could not be inserted");
};
let selection = pets.query().find_age(4).and(|query| query.find_species(2));
assert_eq!(selection.len(), 1);
let mut rows = selection.rows();
let Some(row) = rows.next() else {
return Err("selected pet was unavailable");
};
assert_eq!(
(*row.id, row.name.as_str(), *row.age, *row.species),
(luna_id, "Luna", 4, 2)
);
assert!(rows.next().is_none());
let ages = pets.query().find_age(4).or(|query| query.find_age(7));
assert_eq!(ages.len(), 3);
let all_pets = pets
.query()
.find_species(2)
.or_selection(pets.query().find_species(3));
assert!(ages.is_subset(all_pets));
let absent = pets.query().find_age(99);
assert!(absent.is_empty());
assert!(ages.is_disjoint(absent));
let species_two_ages = ages.and(|query| query.find_species(2));
assert_eq!(species_two_ages.len(), 2);
let fluent_selection = pets
.query()
.find_age(4)
.and(|query| query.find_species(2))
.or(|query| query.find_age(7))
.and_not(|query| query.find_species(3))
.xor(|query| query.find_age(99));
assert_eq!(fluent_selection.len(), 2);
let nested = pets
.query()
.find_species(2)
.and(|query| query.find_age(4).or(|query| query.find_age(7)));
assert_eq!(nested.len(), 2);
let direct_union = pets
.query()
.find_age(4)
.or_selection(pets.query().find_age(7));
assert_eq!(direct_union.len(), 3);
let direct_intersection = pets
.query()
.find_age(4)
.and_selection(pets.query().find_species(2));
assert_eq!(direct_intersection.len(), 1);
let direct_exclusion = pets
.query()
.find_age(4)
.and_not_selection(pets.query().find_species(2));
assert_eq!(direct_exclusion.len(), 1);
let direct_exclusive_union = pets
.query()
.find_age(4)
.xor_selection(pets.query().find_species(2));
let Some(last_exclusive_row) = direct_exclusive_union.rows().next_back() else {
return Err("exclusive selection was unavailable");
};
assert_eq!(*last_exclusive_row.id, suki_id);
let mut absent_rows = pets.query().find_age(99).rows();
assert_eq!(absent_rows.size_hint(), (0, Some(0)));
assert!(absent_rows.next().is_none());
assert!(absent_rows.next_back().is_none());
let non_species_two = pets
.query()
.find_age(4)
.and_not(|query| query.find_species(2));
let non_species_two_ids = non_species_two
.rows()
.map(|row| *row.id)
.collect::<alloc::vec::Vec<_>>();
assert_eq!(non_species_two_ids, [milo_id]);
let age_or_species_but_not_both = pets.query().find_age(4).xor(|query| query.find_species(2));
let exclusive_ids = age_or_species_but_not_both
.rows()
.map(|row| *row.id)
.collect::<alloc::vec::Vec<_>>();
assert_eq!(exclusive_ids, [milo_id, suki_id]);
let species_three = pets.query().find_species(3);
let age_four = pets.query().find_age(4);
let age_seven = pets.query().find_age(7);
assert!(species_three.is_subset(&age_four));
assert!(species_three.is_disjoint(&age_seven));
assert_eq!(
pets.remove(luna_id),
WarpTableRemoveOutcome::Removed {
key: luna_id,
removed: IndexedPet {
name: String::from("Luna"),
age: 4,
species: 2,
},
relocation: WarpTableRowRelocation::Relocated {
key: suki_id,
from: 2,
to: 0,
},
}
);
let Some(row) = pets.query().find_species(2).rows().next() else {
return Err("relocated species-two pet was unavailable");
};
assert_eq!((*row.id, row.name.as_str()), (suki_id, "Suki"));
let Some(row) = pets.query().find_species(3).rows().next() else {
return Err("relocated pet was unavailable");
};
assert_eq!((*row.id, row.name.as_str()), (milo_id, "Milo"));
Ok(())
}
#[cfg(feature = "secondary-indexes")]
#[test]
fn range_indexes_compose_and_follow_table_mutations() -> Result<(), &'static str> {
let capacity = Capacity::new(NonZeroU32::new(4).ok_or("capacity was zero")?);
let mut events = WarpTable::<IndexedEvent>::try_new(capacity)
.map_err(|_| "event storage was unavailable")?;
let WarpTableInsertOutcome::Inserted { key: early_id, .. } =
events.insert(IndexedEvent { tick: 1, kind: 1 })
else {
return Err("early event could not be inserted");
};
let WarpTableInsertOutcome::Inserted { key: middle_id, .. } =
events.insert(IndexedEvent { tick: 5, kind: 1 })
else {
return Err("middle event could not be inserted");
};
let WarpTableInsertOutcome::Inserted { key: late_id, .. } =
events.insert(IndexedEvent { tick: 10, kind: 2 })
else {
return Err("late event could not be inserted");
};
let selected = events
.query()
.find_tick_in(2..=10)
.map_err(|_| "tick range was unavailable")?
.and(|query| query.find_kind(1));
let ids = selected
.rows()
.map(|row| *row.id)
.collect::<alloc::vec::Vec<_>>();
assert_eq!(ids, [middle_id]);
assert_eq!(events.query().find_tick(5).len(), 1);
assert!(matches!(
events.replace(middle_id, IndexedEvent { tick: 20, kind: 2 }),
WarpTableReplaceOutcome::Replaced { .. }
));
assert!(
events
.query()
.find_tick_in(2..=10)
.map_err(|_| "updated tick range was unavailable")?
.and(|query| query.find_kind(1))
.is_empty()
);
assert_eq!(
events
.query()
.find_tick_in(10..)
.map_err(|_| "later tick range was unavailable")?
.len(),
2
);
assert!(matches!(
events.remove(late_id),
WarpTableRemoveOutcome::Removed { .. }
));
let remaining = events
.query()
.find_tick_in(..)
.map_err(|_| "complete tick range was unavailable")?;
assert_eq!(remaining.len(), 2);
let Some(first_remaining) = remaining.rows().next() else {
return Err("event absent");
};
assert_eq!(early_id, *first_remaining.id);
Ok(())
}
#[cfg(feature = "secondary-indexes")]
#[test]
fn replacement_moves_every_changed_secondary_membership() -> Result<(), &'static str> {
let capacity = Capacity::new(NonZeroU32::new(2).ok_or("capacity was zero")?);
let mut pets = WarpTable::<IndexedPet>::try_new(capacity)
.map_err(|_| "indexed pet storage was unavailable")?;
let WarpTableInsertOutcome::Inserted { key: luna_id, .. } = pets.insert(IndexedPet {
name: String::from("Luna"),
age: 4,
species: 2,
}) else {
return Err("Luna could not be inserted");
};
let WarpTableInsertOutcome::Inserted { key: milo_id, .. } = pets.insert(IndexedPet {
name: String::from("Milo"),
age: 4,
species: 3,
}) else {
return Err("Milo could not be inserted");
};
assert!(matches!(
pets.replace(
milo_id,
IndexedPet {
name: String::from("Milo II"),
age: 7,
species: 2,
},
),
WarpTableReplaceOutcome::Replaced { .. }
));
assert_eq!(pets.query().find_age(4).len(), 1);
assert_eq!(pets.query().find_age(7).len(), 1);
let species = pets.query().find_species(2);
assert_eq!(species.len(), 2);
let ids = species
.rows()
.map(|row| *row.id)
.collect::<alloc::vec::Vec<_>>();
assert_eq!(ids, [luna_id, milo_id]);
assert!(matches!(
pets.query().find_species(3),
SecondaryIndexLookupOutcome::Absent { .. }
));
Ok(())
}
#[cfg(feature = "secondary-indexes")]
#[test]
fn selection_rows_fail_fast_for_broken_table_invariants() -> Result<(), &'static str> {
let capacity = Capacity::new(NonZeroU32::MIN);
let mut pets = WarpTable::<IndexedPet>::try_new(capacity)
.map_err(|_| "indexed pet storage was unavailable")?;
let WarpTableInsertOutcome::Inserted { .. } = pets.insert(IndexedPet {
name: String::from("Luna"),
age: 4,
species: 2,
}) else {
return Err("Luna could not be inserted");
};
pets.primary_keys.clear();
assert!(
catch_unwind(AssertUnwindSafe(|| {
let species = pets.query().find_species(2);
species.rows().next()
}))
.is_err()
);
pets.len = 0;
assert!(
catch_unwind(AssertUnwindSafe(|| {
let species = pets.query().find_species(2);
species.rows().next()
}))
.is_err()
);
Ok(())
}