use std::marker::PhantomData;
use crate::{
access::{Access, AccessError, FromAccess},
views::{AsReadonly, GroupKeys, IndexAddress},
BinaryKey,
};
#[derive(Debug)]
pub struct Group<T, K: ?Sized, I> {
access: T,
prefix: IndexAddress,
_key: PhantomData<K>,
_index: PhantomData<I>,
}
impl<T, K, I> FromAccess<T> for Group<T, K, I>
where
T: Access,
K: BinaryKey + ?Sized,
I: FromAccess<T>,
{
fn from_access(access: T, addr: IndexAddress) -> Result<Self, AccessError> {
Ok(Self {
access,
prefix: addr,
_key: PhantomData,
_index: PhantomData,
})
}
}
impl<T, K, I> Group<T, K, I>
where
T: Access,
K: BinaryKey + ?Sized,
I: FromAccess<T>,
{
pub fn get(&self, key: &K) -> I {
let addr = self.prefix.clone().append_key(key);
I::from_access(self.access.clone(), addr)
.unwrap_or_else(|e| panic!("MerkleDB error: {}", e))
}
}
impl<T, K, I> Group<T, K, I>
where
T: Access,
T::Base: AsReadonly<Readonly = T::Base>,
K: BinaryKey + ?Sized,
{
pub fn keys(&self) -> GroupKeys<T::Base, K> {
self.access.clone().group_keys(self.prefix.clone())
}
}
#[cfg(test)]
mod tests {
use super::{Access, AsReadonly, BinaryKey, FromAccess, Group};
use crate::{
access::{AccessExt, CopyAccessExt, Prefixed, RawAccessMut},
migration::{Migration, Scratchpad},
Database, ListIndex, TemporaryDB,
};
#[test]
fn group() {
let db = TemporaryDB::new();
let fork = db.fork();
{
let group: Group<_, u32, ListIndex<_, String>> = fork.get_group("group");
let mut list = group.get(&1);
list.push("foo".to_owned());
list.push("bar".to_owned());
group.get(&2).push("baz".to_owned());
}
{
let list = fork.get_list::<_, String>(("group", &1_u32));
assert_eq!(list.len(), 2);
assert_eq!(list.get(1), Some("bar".to_owned()));
let other_list = fork.get_list::<_, String>(("group", &2_u32));
assert_eq!(other_list.len(), 1);
}
db.merge_sync(fork.into_patch()).unwrap();
let snapshot = db.snapshot();
let group: Group<_, u32, ListIndex<_, String>> = snapshot.get_group("group");
assert_eq!(group.get(&1).len(), 2);
assert_eq!(group.get(&2).len(), 1);
assert!(group.get(&0).is_empty());
}
fn prepare_key_iter<A>(fork: &A)
where
A: Access,
A::Base: RawAccessMut,
{
let group: Group<_, str, ListIndex<_, String>> = fork.get_group("group");
group.get("foo").push("foo".to_owned());
group.get("bar").push("bar".to_owned());
group.get("baz").push("baz".to_owned());
let group: Group<_, u32, ListIndex<_, String>> =
Group::from_access(fork.clone(), ("prefixed", &0_u8).into()).unwrap();
group.get(&1).push("foo".to_owned());
group.get(&2).push("bar".to_owned());
group.get(&5).push("baz".to_owned());
group.get(&100_000).push("?".to_owned());
fork.get_entry("gr").set(42);
fork.get_entry("group_").set("!".to_owned());
fork.get_list(("group_", &1_u8)).extend(vec![1, 2, 3]);
fork.get_entry("prefix").set(".".to_owned());
fork.get_entry("prefixed").set("??".to_owned());
fork.get_list(("prefixed", &1_u8)).push(42);
fork.get_entry(("prefixed", &concat_keys!(&1_u8, &42_u32)))
.set(42);
fork.get_entry("t").set(21);
fork.get_entry("unrelated").set(23);
}
fn test_key_iter<A>(snapshot: A)
where
A: Access,
A::Base: AsReadonly<Readonly = A::Base>,
{
let group: Group<_, str, ListIndex<_, String>> = snapshot.get_group("group");
assert_eq!(
group.keys().collect::<Vec<_>>(),
vec!["bar".to_owned(), "baz".to_owned(), "foo".to_owned()]
);
let group: Group<_, u32, ListIndex<_, String>> =
Group::from_access(snapshot, ("prefixed", &0_u8).into()).unwrap();
assert_eq!(group.keys().collect::<Vec<_>>(), vec![1, 2, 5, 100_000]);
}
#[test]
fn iterating_over_keys() {
let db = TemporaryDB::new();
let fork = db.fork();
prepare_key_iter(&&fork);
test_key_iter(fork.readonly());
let patch = fork.into_patch();
test_key_iter(&patch);
}
#[test]
fn iterating_over_keys_in_prefixed_access() {
let db = TemporaryDB::new();
let fork = db.fork();
prepare_key_iter(&Prefixed::new("namespace", &fork));
test_key_iter(Prefixed::new("namespace", fork.readonly()));
let patch = fork.into_patch();
test_key_iter(Prefixed::new("namespace", &patch));
db.merge(patch).unwrap();
test_key_iter(Prefixed::new("namespace", &db.snapshot()));
}
#[test]
fn iterating_over_keys_in_migration() {
let db = TemporaryDB::new();
let fork = db.fork();
prepare_key_iter(&Migration::new("namespace", &fork));
test_key_iter(Migration::new("namespace", fork.readonly()));
let patch = fork.into_patch();
test_key_iter(Migration::new("namespace", &patch));
db.merge(patch).unwrap();
test_key_iter(Migration::new("namespace", &db.snapshot()));
}
#[test]
fn iterating_over_keys_in_scratchpad() {
let db = TemporaryDB::new();
let fork = db.fork();
prepare_key_iter(&Scratchpad::new("namespace", &fork));
test_key_iter(Scratchpad::new("namespace", fork.readonly()));
let patch = fork.into_patch();
test_key_iter(Scratchpad::new("namespace", &patch));
db.merge(patch).unwrap();
test_key_iter(Scratchpad::new("namespace", &db.snapshot()));
}
}