use std::rc::Rc;
use crate::{
access::{Access, AccessError, AsReadonly, Prefixed},
db::{ChangesMut, ChangesRef, ViewChanges},
migration::{Migration, Scratchpad},
views::{ChangeSet, GroupKeys, IndexMetadata, RawAccess, RawAccessMut, ViewWithMetadata},
BinaryKey, Fork, IndexAddress, IndexType, OwnedReadonlyFork, ReadonlyFork, ResolvedAddress,
Snapshot,
};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum GenericRawAccess<'a> {
Snapshot(&'a dyn Snapshot),
OwnedSnapshot(Rc<dyn Snapshot>),
Fork(&'a Fork),
OwnedFork(Rc<Fork>),
ReadonlyFork(ReadonlyFork<'a>),
OwnedReadonlyFork(OwnedReadonlyFork),
}
impl GenericRawAccess<'_> {
pub fn is_mutable(&self) -> bool {
match self {
GenericRawAccess::Fork(_) | GenericRawAccess::OwnedFork(_) => true,
_ => false,
}
}
}
impl<'a> From<&'a dyn Snapshot> for GenericRawAccess<'a> {
fn from(snapshot: &'a dyn Snapshot) -> Self {
GenericRawAccess::Snapshot(snapshot)
}
}
impl From<Box<dyn Snapshot>> for GenericRawAccess<'_> {
fn from(snapshot: Box<dyn Snapshot>) -> Self {
GenericRawAccess::OwnedSnapshot(Rc::from(snapshot))
}
}
impl<'a> From<&'a Fork> for GenericRawAccess<'a> {
fn from(fork: &'a Fork) -> Self {
GenericRawAccess::Fork(fork)
}
}
impl From<Fork> for GenericRawAccess<'_> {
fn from(fork: Fork) -> Self {
GenericRawAccess::OwnedFork(Rc::new(fork))
}
}
impl<'a> From<ReadonlyFork<'a>> for GenericRawAccess<'a> {
fn from(ro_fork: ReadonlyFork<'a>) -> Self {
GenericRawAccess::ReadonlyFork(ro_fork)
}
}
impl From<OwnedReadonlyFork> for GenericRawAccess<'_> {
fn from(ro_fork: OwnedReadonlyFork) -> Self {
GenericRawAccess::OwnedReadonlyFork(ro_fork)
}
}
impl AsReadonly for GenericRawAccess<'_> {
type Readonly = Self;
fn as_readonly(&self) -> Self::Readonly {
use self::GenericRawAccess::*;
match self {
Snapshot(snapshot) => Snapshot(*snapshot),
OwnedSnapshot(snapshot) => OwnedSnapshot(Rc::clone(snapshot)),
ReadonlyFork(ro_fork) => ReadonlyFork(*ro_fork),
OwnedReadonlyFork(ro_fork) => OwnedReadonlyFork(ro_fork.clone()),
Fork(fork) => ReadonlyFork(fork.readonly()),
OwnedFork(fork) => OwnedReadonlyFork(fork.as_readonly()),
}
}
}
#[doc(hidden)]
#[derive(Debug)]
pub enum GenericChanges<'a> {
None,
Ref(ChangesRef<'a>),
Mut(ChangesMut<'a>),
}
impl ChangeSet for GenericChanges<'_> {
fn as_ref(&self) -> Option<&ViewChanges> {
match self {
GenericChanges::None => None,
GenericChanges::Ref(changes) => Some(&*changes),
GenericChanges::Mut(changes) => Some(&*changes),
}
}
fn as_mut(&mut self) -> Option<&mut ViewChanges> {
match self {
GenericChanges::None | GenericChanges::Ref(_) => None,
GenericChanges::Mut(changes) => Some(&mut *changes),
}
}
}
impl<'a> RawAccess for GenericRawAccess<'a> {
type Changes = GenericChanges<'a>;
fn snapshot(&self) -> &dyn Snapshot {
match self {
GenericRawAccess::Snapshot(snapshot) => *snapshot,
GenericRawAccess::OwnedSnapshot(snapshot) => snapshot.as_ref(),
GenericRawAccess::Fork(fork) => fork.snapshot(),
GenericRawAccess::OwnedFork(fork) => fork.snapshot(),
GenericRawAccess::ReadonlyFork(ro_fork) => ro_fork.snapshot(),
GenericRawAccess::OwnedReadonlyFork(ro_fork) => ro_fork.snapshot(),
}
}
fn changes(&self, address: &ResolvedAddress) -> Self::Changes {
match self {
GenericRawAccess::Snapshot(_) | GenericRawAccess::OwnedSnapshot(_) => {
GenericChanges::None
}
GenericRawAccess::Fork(fork) => GenericChanges::Mut(fork.changes(address)),
GenericRawAccess::OwnedFork(fork) => GenericChanges::Mut(fork.changes(address)),
GenericRawAccess::ReadonlyFork(ro_fork) => {
GenericChanges::Ref(ro_fork.changes(address))
}
GenericRawAccess::OwnedReadonlyFork(ro_fork) => {
GenericChanges::Ref(ro_fork.changes(address))
}
}
}
}
impl RawAccessMut for GenericRawAccess<'_> {}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum GenericAccess<T> {
Raw(T),
Prefixed(Prefixed<T>),
Migration(Migration<T>),
Scratchpad(Scratchpad<T>),
}
impl<T: RawAccess> From<T> for GenericAccess<T> {
fn from(access: T) -> Self {
Self::Raw(access)
}
}
impl<T: RawAccess> From<Prefixed<T>> for GenericAccess<T> {
fn from(access: Prefixed<T>) -> Self {
Self::Prefixed(access)
}
}
impl<T: RawAccess> From<Migration<T>> for GenericAccess<T> {
fn from(access: Migration<T>) -> Self {
Self::Migration(access)
}
}
impl<T: RawAccess> From<Scratchpad<T>> for GenericAccess<T> {
fn from(access: Scratchpad<T>) -> Self {
Self::Scratchpad(access)
}
}
impl<T: RawAccess> Access for GenericAccess<T> {
type Base = T;
fn get_index_metadata(
self,
addr: IndexAddress,
) -> Result<Option<IndexMetadata<Vec<u8>>>, AccessError> {
match self {
Self::Raw(access) => access.get_index_metadata(addr),
Self::Prefixed(access) => access.get_index_metadata(addr),
Self::Migration(access) => access.get_index_metadata(addr),
Self::Scratchpad(access) => access.get_index_metadata(addr),
}
}
fn get_or_create_view(
self,
addr: IndexAddress,
index_type: IndexType,
) -> Result<ViewWithMetadata<Self::Base>, AccessError> {
match self {
Self::Raw(access) => access.get_or_create_view(addr, index_type),
Self::Prefixed(access) => access.get_or_create_view(addr, index_type),
Self::Migration(access) => access.get_or_create_view(addr, index_type),
Self::Scratchpad(access) => access.get_or_create_view(addr, index_type),
}
}
fn group_keys<K>(self, base_addr: IndexAddress) -> GroupKeys<Self::Base, K>
where
K: BinaryKey + ?Sized,
Self::Base: AsReadonly<Readonly = Self::Base>,
{
match self {
Self::Raw(access) => access.group_keys(base_addr),
Self::Prefixed(access) => access.group_keys(base_addr),
Self::Migration(access) => access.group_keys(base_addr),
Self::Scratchpad(access) => access.group_keys(base_addr),
}
}
}
pub type ErasedAccess<'a> = GenericAccess<GenericRawAccess<'a>>;
impl ErasedAccess<'_> {
pub fn is_mutable(&self) -> bool {
match self {
Self::Raw(access) => access.is_mutable(),
Self::Prefixed(prefixed) => prefixed.access().is_mutable(),
Self::Migration(migration) => migration.access().is_mutable(),
Self::Scratchpad(scratchpad) => scratchpad.access().is_mutable(),
}
}
}
pub trait IntoErased<'a> {
fn into_erased(self) -> ErasedAccess<'a>;
}
impl<'a> IntoErased<'a> for &'a dyn Snapshot {
fn into_erased(self) -> ErasedAccess<'a> {
GenericAccess::Raw(GenericRawAccess::from(self))
}
}
impl<'a> IntoErased<'a> for &'a Fork {
fn into_erased(self) -> ErasedAccess<'a> {
GenericAccess::Raw(GenericRawAccess::from(self))
}
}
impl<'a> IntoErased<'a> for ReadonlyFork<'a> {
fn into_erased(self) -> ErasedAccess<'a> {
GenericAccess::Raw(GenericRawAccess::from(self))
}
}
#[allow(clippy::use_self)]
impl<'a, T> IntoErased<'a> for Prefixed<T>
where
T: Into<GenericRawAccess<'a>>,
{
fn into_erased(self) -> ErasedAccess<'a> {
let (prefix, access) = self.into_parts();
let access: GenericRawAccess<'_> = access.into();
GenericAccess::Prefixed(Prefixed::new(prefix, access))
}
}
#[allow(clippy::use_self)]
impl<'a, T> IntoErased<'a> for Migration<T>
where
T: Into<GenericRawAccess<'a>>,
{
fn into_erased(self) -> ErasedAccess<'a> {
let (prefix, access) = self.into_parts();
let access: GenericRawAccess<'_> = access.into();
GenericAccess::Migration(Migration::new(prefix, access))
}
}
#[allow(clippy::use_self)]
impl<'a, T> IntoErased<'a> for Scratchpad<T>
where
T: Into<GenericRawAccess<'a>>,
{
fn into_erased(self) -> ErasedAccess<'a> {
let (prefix, access) = self.into_parts();
let access: GenericRawAccess<'_> = access.into();
GenericAccess::Scratchpad(Scratchpad::new(prefix, access))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
access::{AccessExt, CopyAccessExt},
Database, TemporaryDB,
};
#[test]
fn generic_raw_access() {
let db = TemporaryDB::new();
let fork = db.fork();
{
let access = GenericRawAccess::from(&fork);
assert!(access.is_mutable());
let mut list = access.get_proof_list("list");
list.extend(vec![1_u32, 2, 3]);
access.get_entry("entry").set("!".to_owned());
}
{
let access = GenericRawAccess::from(fork.readonly());
assert!(!access.is_mutable());
let list = access.get_proof_list::<_, u32>("list");
assert_eq!(list.len(), 3);
assert_eq!(list.iter().collect::<Vec<_>>(), vec![1, 2, 3]);
let non_existent_map = access.get_map::<_, u32, u32>("map");
assert_eq!(non_existent_map.get(&1), None);
let non_existent_list = access.get_list::<_, u32>("other_list");
assert_eq!(non_existent_list.len(), 0);
}
let patch = fork.into_patch();
let access = GenericRawAccess::from(&patch as &dyn Snapshot);
assert!(!access.is_mutable());
assert_eq!(access.get_entry::<_, String>("entry").get().unwrap(), "!");
db.merge(patch).unwrap();
let snapshot = db.snapshot();
let access = GenericRawAccess::from(snapshot.as_ref());
assert!(!access.is_mutable());
let list = access.get_proof_list::<_, u32>("list");
assert_eq!(list.len(), 3);
assert_eq!(list.iter().collect::<Vec<_>>(), vec![1, 2, 3]);
let non_existent_map = access.get_map::<_, u32, u32>("map");
assert_eq!(non_existent_map.get(&1), None);
let non_existent_list = access.get_list::<_, u32>("other_list");
assert_eq!(non_existent_list.len(), 0);
}
#[test]
fn generic_raw_owned_access() {
let db = TemporaryDB::new();
let fork = db.fork();
let access = GenericRawAccess::from(fork);
assert!(access.is_mutable());
{
let mut list = access.get_proof_list("list");
list.extend(vec![1_u32, 2, 3]);
access.get_entry("entry").set("!".to_owned());
}
let fork = match access {
GenericRawAccess::OwnedFork(fork) => Rc::try_unwrap(fork).unwrap(),
_ => unreachable!(),
};
db.merge(fork.into_patch()).unwrap();
let access = GenericRawAccess::from(db.snapshot());
assert!(!access.is_mutable());
let list = access.get_proof_list::<_, u32>("list");
assert_eq!(list.len(), 3);
assert_eq!(list.iter().collect::<Vec<_>>(), vec![1, 2, 3]);
assert_eq!(access.get_entry::<_, String>("entry").get().unwrap(), "!");
let non_existent_map = access.get_map::<_, u32, u32>("map");
assert_eq!(non_existent_map.get(&1), None);
let non_existent_list = access.get_list::<_, u32>("other_list");
assert_eq!(non_existent_list.len(), 0);
}
#[test]
#[should_panic(expected = "Attempt to modify a readonly view of the database")]
fn generic_raw_access_panic_on_non_existing_index() {
let db = TemporaryDB::new();
let snapshot = db.snapshot();
let access = GenericRawAccess::from(snapshot.as_ref());
let mut list = access.get_list::<_, u32>("list");
list.push(1);
}
#[test]
#[should_panic(expected = "Attempt to modify a readonly view of the database")]
fn generic_raw_access_panic_on_existing_index() {
let db = TemporaryDB::new();
let fork = db.fork();
fork.get_entry("entry").set(1_u8);
let access = GenericRawAccess::from(fork.readonly());
access.get_entry("entry").set(2_u8);
}
#[test]
#[should_panic(expected = "Attempt to modify a readonly view of the database")]
fn generic_raw_access_as_readonly() {
let db = TemporaryDB::new();
let fork = db.fork();
fork.get_proof_list("list").extend(vec![1_u32, 2, 3]);
let access = GenericRawAccess::from(&fork);
let readonly = access.as_readonly();
assert!(!readonly.is_mutable());
let mut list = readonly.get_proof_list::<_, u32>("list");
assert_eq!(list.len(), 3);
assert_eq!(list.get(1), Some(2));
list.push(4);
}
#[test]
#[should_panic(expected = "Attempt to modify a readonly view of the database")]
fn generic_raw_access_as_static_readonly() {
let db = TemporaryDB::new();
let fork = db.fork();
fork.get_proof_list("list").extend(vec![1_u32, 2, 3]);
let access = GenericRawAccess::from(fork);
let readonly = access.as_readonly();
assert!(!readonly.is_mutable());
let mut list = readonly.get_proof_list::<_, u32>("list");
assert_eq!(list.len(), 3);
assert_eq!(list.get(1), Some(2));
list.push(4);
}
#[test]
fn generic_access_workflow() {
let db = TemporaryDB::new();
let fork = db.fork();
let access = Prefixed::new("foo", &fork).into_erased();
assert!(access.is_mutable());
access.get_list("list").extend(vec![2_u32, 3, 4]);
access.get_proof_map("map").put("foo", 42_u64);
access.get_value_set("set").insert(100_u8);
let access = (&fork).into_erased();
assert!(access.is_mutable());
assert_eq!(access.get_list::<_, u32>("foo.list").len(), 3);
assert_eq!(
access.get_proof_map::<_, str, u64>("foo.map").get("foo"),
Some(42)
);
assert!(access.get_value_set::<_, u8>("foo.set").contains(&100));
let access = Prefixed::new("foo", fork.readonly()).into_erased();
assert!(!access.is_mutable());
assert_eq!(access.get_list::<_, u32>("list").len(), 3);
assert_eq!(
access.get_proof_map::<_, str, u64>("map").get("foo"),
Some(42)
);
assert!(access.get_value_set::<_, u8>("set").contains(&100));
let access = Migration::new("foo", &fork).into_erased();
assert!(access.is_mutable());
access.get_proof_list("list").extend(vec![4_i32, 5, 6, 7]);
access.get_key_set("set").insert(&99_u8);
let access = Scratchpad::new("foo", &fork).into_erased();
access.get_entry("iter_position").set(123_u32);
drop(access);
let patch = fork.into_patch();
let patch_ref = &patch as &dyn Snapshot;
let access = Migration::new("foo", patch_ref).into_erased();
assert!(!access.is_mutable());
let list = access.get_proof_list::<_, i32>("list");
assert_eq!(list.len(), 4);
assert_eq!(list.iter().collect::<Vec<_>>(), vec![4, 5, 6, 7]);
let set = access.get_key_set::<_, u8>("set");
assert_eq!(set.iter().collect::<Vec<_>>(), vec![99]);
let erased = Scratchpad::new("foo", patch_ref).into_erased();
assert!(!access.is_mutable());
assert_eq!(erased.get_entry::<_, u32>("iter_position").get(), Some(123));
}
}