use std::fmt::Debug;
use std::marker::PhantomData;
use namada_core::storage::{self, DbKeySeg, KeySeg};
use thiserror::Error;
use super::super::Result;
use super::{LazyCollection, ReadError};
use crate::{ResultExt, StorageRead, StorageWrite};
#[derive(Debug)]
pub struct LazySet<K> {
key: storage::Key,
phantom_k: PhantomData<K>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum SubKey<K> {
Data(K),
}
#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum ValidationError {
#[error("Invalid storage key {0}")]
InvalidSubKey(storage::Key),
}
impl<K> LazyCollection for LazySet<K>
where
K: storage::KeySeg + Debug,
{
type SubKey = SubKey<K>;
type Value = ();
fn open(key: storage::Key) -> Self {
Self {
key,
phantom_k: PhantomData,
}
}
fn is_valid_sub_key(
&self,
key: &storage::Key,
) -> crate::Result<Option<Self::SubKey>> {
let suffix = match key.split_prefix(&self.key) {
None => {
return Ok(None);
}
Some(None) => {
return Err(ValidationError::InvalidSubKey(key.clone()))
.into_storage_result();
}
Some(Some(suffix)) => suffix,
};
let validate_sub_key = |raw_sub_key| {
if let Ok(key) = storage::KeySeg::parse(raw_sub_key) {
Ok(Some(SubKey::Data(key)))
} else {
Err(ValidationError::InvalidSubKey(key.clone()))
.into_storage_result()
}
};
match &suffix.segments[..] {
[DbKeySeg::StringSeg(sub)] => validate_sub_key(sub.clone()),
[DbKeySeg::AddressSeg(sub)] => validate_sub_key(sub.raw()),
_ => Err(ValidationError::InvalidSubKey(key.clone()))
.into_storage_result(),
}
}
fn is_data_sub_key(&self, key: &storage::Key) -> bool {
matches!(self.is_valid_sub_key(key), Ok(Some(_)))
}
}
impl<K> LazySet<K>
where
K: storage::KeySeg,
{
pub fn contains<S>(&self, storage: &S, key: &K) -> Result<bool>
where
S: StorageRead,
{
storage.has_key(&self.get_key(key))
}
pub fn get_key(&self, key: &K) -> storage::Key {
let key_str = key.to_db_key();
self.key.push(&key_str).unwrap()
}
pub fn insert<S>(&self, storage: &mut S, key: K) -> Result<bool>
where
S: StorageWrite + StorageRead,
{
let present = self.contains(storage, &key)?;
let key = self.get_key(&key);
storage.write(&key, ())?;
Ok(present)
}
pub fn try_insert<S>(&self, storage: &mut S, key: K) -> Result<()>
where
S: StorageWrite + StorageRead,
{
let present = self.contains(storage, &key)?;
if present {
return Err(crate::Error::new_const("Occupied"));
}
let key = self.get_key(&key);
storage.write(&key, ())
}
pub fn remove<S>(&self, storage: &mut S, key: &K) -> Result<bool>
where
S: StorageWrite + StorageRead,
{
let present = self.contains(storage, key)?;
if present {
let key = self.get_key(key);
storage.delete(&key)?;
}
Ok(present)
}
pub fn is_empty<S>(&self, storage: &S) -> Result<bool>
where
S: StorageRead,
{
let mut iter = crate::iter_prefix_bytes(storage, &self.key)?;
Ok(iter.next().is_none())
}
#[allow(clippy::len_without_is_empty)]
pub fn len<S>(&self, storage: &S) -> Result<u64>
where
S: StorageRead,
{
let iter = crate::iter_prefix_bytes(storage, &self.key)?;
iter.count().try_into().into_storage_result()
}
pub fn iter<'iter>(
&self,
storage: &'iter impl StorageRead,
) -> Result<impl Iterator<Item = Result<K>> + 'iter> {
let iter = crate::iter_prefix(storage, &self.key)?;
Ok(iter.map(|key_val_res| {
let (key, ()) = key_val_res?;
let last_key_seg = key
.last()
.ok_or(ReadError::UnexpectedlyEmptyStorageKey)
.into_storage_result()?;
let key = K::parse(last_key_seg.raw()).into_storage_result()?;
Ok(key)
}))
}
}
#[cfg(test)]
mod test {
use namada_core::address::{self, Address};
use super::*;
use crate::testing::TestStorage;
#[test]
fn test_lazy_set_basics() -> crate::Result<()> {
let mut storage = TestStorage::default();
let key = storage::Key::parse("test").unwrap();
let lazy_set = LazySet::<u32>::open(key);
assert!(lazy_set.is_empty(&storage)?);
assert!(lazy_set.len(&storage)? == 0);
assert!(!lazy_set.contains(&storage, &0)?);
assert!(!lazy_set.contains(&storage, &1)?);
assert!(lazy_set.iter(&storage)?.next().is_none());
assert!(!lazy_set.remove(&mut storage, &0)?);
assert!(!lazy_set.remove(&mut storage, &1)?);
let key = 123;
lazy_set.insert(&mut storage, key)?;
let key2 = 456;
lazy_set.insert(&mut storage, key2)?;
let key3 = 256;
lazy_set.try_insert(&mut storage, key3).unwrap();
assert!(!lazy_set.contains(&storage, &0)?);
assert!(lazy_set.contains(&storage, &key)?);
assert!(!lazy_set.is_empty(&storage)?);
assert!(lazy_set.len(&storage)? == 3);
let mut set_it = lazy_set.iter(&storage)?;
assert_eq!(set_it.next().unwrap()?, key);
assert_eq!(set_it.next().unwrap()?, key3);
assert_eq!(set_it.next().unwrap()?, key2);
drop(set_it);
assert!(!lazy_set.contains(&storage, &0)?);
assert!(lazy_set.contains(&storage, &key)?);
assert!(lazy_set.contains(&storage, &key2)?);
assert!(lazy_set.try_insert(&mut storage, key3).is_err());
let removed = lazy_set.remove(&mut storage, &key)?;
assert!(removed);
assert!(!lazy_set.is_empty(&storage)?);
assert!(lazy_set.len(&storage)? == 2);
assert!(!lazy_set.contains(&storage, &0)?);
assert!(!lazy_set.contains(&storage, &1)?);
assert!(!lazy_set.contains(&storage, &123)?);
assert!(lazy_set.contains(&storage, &456)?);
assert!(!lazy_set.contains(&storage, &key)?);
assert!(lazy_set.contains(&storage, &key2)?);
assert!(lazy_set.iter(&storage)?.next().is_some());
assert!(!lazy_set.remove(&mut storage, &key)?);
let removed = lazy_set.remove(&mut storage, &key2)?;
assert!(removed);
assert!(lazy_set.len(&storage)? == 1);
let removed = lazy_set.remove(&mut storage, &key3)?;
assert!(removed);
assert!(lazy_set.is_empty(&storage)?);
assert!(lazy_set.len(&storage)? == 0);
assert!(lazy_set.try_insert(&mut storage, key).is_ok());
assert!(lazy_set.try_insert(&mut storage, key).is_err());
let storage_key = lazy_set.get_key(&key);
assert_eq!(
lazy_set.is_valid_sub_key(&storage_key).unwrap(),
Some(SubKey::Data(key))
);
let storage_key2 = lazy_set.get_key(&key2);
assert_eq!(
lazy_set.is_valid_sub_key(&storage_key2).unwrap(),
Some(SubKey::Data(key2))
);
Ok(())
}
#[test]
fn test_lazy_set_with_addr_key() -> crate::Result<()> {
let mut storage = TestStorage::default();
let key = storage::Key::parse("test").unwrap();
let lazy_set = LazySet::<Address>::open(key);
let key = address::testing::established_address_1();
lazy_set.insert(&mut storage, key.clone())?;
assert_eq!(lazy_set.len(&storage)?, 1);
let mut map_it = lazy_set.iter(&storage)?;
assert_eq!(map_it.next().unwrap()?, key);
drop(map_it);
let key2 = address::testing::established_address_2();
lazy_set.insert(&mut storage, key2.clone())?;
assert_eq!(lazy_set.len(&storage)?, 2);
let mut iter = lazy_set.iter(&storage)?;
assert!(key < key2, "sanity check - this influences the iter order");
assert_eq!(iter.next().unwrap()?, key);
assert_eq!(iter.next().unwrap()?, key2);
assert!(iter.next().is_none());
drop(iter);
let storage_key = lazy_set.get_key(&key);
assert_eq!(
lazy_set.is_valid_sub_key(&storage_key).unwrap(),
Some(SubKey::Data(key))
);
let storage_key2 = lazy_set.get_key(&key2);
assert_eq!(
lazy_set.is_valid_sub_key(&storage_key2).unwrap(),
Some(SubKey::Data(key2))
);
Ok(())
}
}