#![cfg(feature = "iterator")]
use crate::PrefixBound;
use cosmwasm_std::{StdError, StdResult, Storage};
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::de::KeyDeserialize;
use crate::indexes::Index;
use crate::iter_helpers::{deserialize_kv, deserialize_v};
use crate::keys::{Prefixer, PrimaryKey};
use crate::map::Map;
use crate::prefix::{namespaced_prefix_range, Prefix};
use crate::{Bound, Path};
pub trait IndexList<T> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<T>> + '_>;
}
pub struct IndexedMap<'a, K, T, I>
where
K: PrimaryKey<'a>,
T: Serialize + DeserializeOwned + Clone,
I: IndexList<T>,
{
pk_namespace: &'a [u8],
primary: Map<'a, K, T>,
pub idx: I,
}
impl<'a, K, T, I> IndexedMap<'a, K, T, I>
where
K: PrimaryKey<'a>,
T: Serialize + DeserializeOwned + Clone,
I: IndexList<T>,
{
pub const fn new(pk_namespace: &'a str, indexes: I) -> Self {
IndexedMap {
pk_namespace: pk_namespace.as_bytes(),
primary: Map::new(pk_namespace),
idx: indexes,
}
}
pub fn key(&self, k: K) -> Path<T> {
self.primary.key(k)
}
}
impl<'a, K, T, I> IndexedMap<'a, K, T, I>
where
K: PrimaryKey<'a>,
T: Serialize + DeserializeOwned + Clone,
I: IndexList<T>,
{
pub fn save(&self, store: &mut dyn Storage, key: K, data: &T) -> StdResult<()> {
let old_data = self.may_load(store, key.clone())?;
self.replace(store, key, Some(data), old_data.as_ref())
}
pub fn remove(&self, store: &mut dyn Storage, key: K) -> StdResult<()> {
let old_data = self.may_load(store, key.clone())?;
self.replace(store, key, None, old_data.as_ref())
}
pub fn replace(
&self,
store: &mut dyn Storage,
key: K,
data: Option<&T>,
old_data: Option<&T>,
) -> StdResult<()> {
let pk = key.joined_key();
if let Some(old) = old_data {
for index in self.idx.get_indexes() {
index.remove(store, &pk, old)?;
}
}
if let Some(updated) = data {
for index in self.idx.get_indexes() {
index.save(store, &pk, updated)?;
}
self.primary.save(store, key, updated)?;
} else {
self.primary.remove(store, key);
}
Ok(())
}
pub fn update<A, E>(&self, store: &mut dyn Storage, key: K, action: A) -> Result<T, E>
where
A: FnOnce(Option<T>) -> Result<T, E>,
E: From<StdError>,
{
let input = self.may_load(store, key.clone())?;
let old_val = input.clone();
let output = action(input)?;
self.replace(store, key, Some(&output), old_val.as_ref())?;
Ok(output)
}
pub fn load(&self, store: &dyn Storage, key: K) -> StdResult<T> {
self.primary.load(store, key)
}
pub fn may_load(&self, store: &dyn Storage, key: K) -> StdResult<Option<T>> {
self.primary.may_load(store, key)
}
pub fn has(&self, store: &dyn Storage, k: K) -> bool {
self.primary.key(k).has(store)
}
fn no_prefix_raw(&self) -> Prefix<Vec<u8>, T, K> {
Prefix::new(self.pk_namespace, &[])
}
pub fn clear(&self, store: &mut dyn Storage) {
const TAKE: usize = 10;
let mut cleared = false;
while !cleared {
let paths = self
.no_prefix_raw()
.keys_raw(store, None, None, cosmwasm_std::Order::Ascending)
.map(|raw_key| Path::<T>::new(self.pk_namespace, &[raw_key.as_slice()]))
.take(TAKE)
.collect::<Vec<_>>();
paths.iter().for_each(|path| store.remove(path));
cleared = paths.len() < TAKE;
}
}
pub fn is_empty(&self, store: &dyn Storage) -> bool {
self.no_prefix_raw()
.keys_raw(store, None, None, cosmwasm_std::Order::Ascending)
.next()
.is_none()
}
}
#[cfg(feature = "iterator")]
impl<'a, K, T, I> IndexedMap<'a, K, T, I>
where
K: PrimaryKey<'a>,
T: Serialize + DeserializeOwned + Clone,
I: IndexList<T>,
{
pub fn prefix_range_raw<'c>(
&self,
store: &'c dyn Storage,
min: Option<PrefixBound<'a, K::Prefix>>,
max: Option<PrefixBound<'a, K::Prefix>>,
order: cosmwasm_std::Order,
) -> Box<dyn Iterator<Item = StdResult<cosmwasm_std::Record<T>>> + 'c>
where
T: 'c,
'a: 'c,
{
let mapped =
namespaced_prefix_range(store, self.pk_namespace, min, max, order).map(deserialize_v);
Box::new(mapped)
}
}
#[cfg(feature = "iterator")]
impl<'a, K, T, I> IndexedMap<'a, K, T, I>
where
T: Serialize + DeserializeOwned + Clone,
K: PrimaryKey<'a>,
I: IndexList<T>,
{
pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix<K::SuperSuffix, T, K::SuperSuffix> {
Prefix::new(self.pk_namespace, &p.prefix())
}
pub fn prefix(&self, p: K::Prefix) -> Prefix<K::Suffix, T, K::Suffix> {
Prefix::new(self.pk_namespace, &p.prefix())
}
}
#[cfg(feature = "iterator")]
impl<'a, K, T, I> IndexedMap<'a, K, T, I>
where
T: Serialize + DeserializeOwned + Clone,
K: PrimaryKey<'a> + KeyDeserialize,
I: IndexList<T>,
{
pub fn prefix_range<'c>(
&self,
store: &'c dyn Storage,
min: Option<PrefixBound<'a, K::Prefix>>,
max: Option<PrefixBound<'a, K::Prefix>>,
order: cosmwasm_std::Order,
) -> Box<dyn Iterator<Item = StdResult<(K::Output, T)>> + 'c>
where
T: 'c,
'a: 'c,
K: 'c,
K::Output: 'static,
{
let mapped = namespaced_prefix_range(store, self.pk_namespace, min, max, order)
.map(deserialize_kv::<K, T>);
Box::new(mapped)
}
pub fn range_raw<'c>(
&self,
store: &'c dyn Storage,
min: Option<Bound<'a, K>>,
max: Option<Bound<'a, K>>,
order: cosmwasm_std::Order,
) -> Box<dyn Iterator<Item = StdResult<cosmwasm_std::Record<T>>> + 'c>
where
T: 'c,
{
self.no_prefix_raw().range_raw(store, min, max, order)
}
pub fn keys_raw<'c>(
&self,
store: &'c dyn Storage,
min: Option<Bound<'a, K>>,
max: Option<Bound<'a, K>>,
order: cosmwasm_std::Order,
) -> Box<dyn Iterator<Item = Vec<u8>> + 'c> {
self.no_prefix_raw().keys_raw(store, min, max, order)
}
pub fn range<'c>(
&self,
store: &'c dyn Storage,
min: Option<Bound<'a, K>>,
max: Option<Bound<'a, K>>,
order: cosmwasm_std::Order,
) -> Box<dyn Iterator<Item = StdResult<(K::Output, T)>> + 'c>
where
T: 'c,
K::Output: 'static,
{
self.no_prefix().range(store, min, max, order)
}
pub fn keys<'c>(
&self,
store: &'c dyn Storage,
min: Option<Bound<'a, K>>,
max: Option<Bound<'a, K>>,
order: cosmwasm_std::Order,
) -> Box<dyn Iterator<Item = StdResult<K::Output>> + 'c>
where
T: 'c,
K::Output: 'static,
{
self.no_prefix().keys(store, min, max, order)
}
fn no_prefix(&self) -> Prefix<K, T, K> {
Prefix::new(self.pk_namespace, &[])
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::indexes::test::{index_string_tuple, index_tuple};
use crate::{MultiIndex, UniqueIndex};
use cosmwasm_std::testing::MockStorage;
use cosmwasm_std::{MemoryStorage, Order};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Data {
pub name: String,
pub last_name: String,
pub age: u32,
}
struct DataIndexes<'a> {
pub name: MultiIndex<'a, String, Data, String>,
pub age: UniqueIndex<'a, u32, Data, String>,
pub name_lastname: UniqueIndex<'a, (Vec<u8>, Vec<u8>), Data, String>,
}
impl<'a> IndexList<Data> for DataIndexes<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<Data>> + '_> {
let v: Vec<&dyn Index<Data>> = vec![&self.name, &self.age, &self.name_lastname];
Box::new(v.into_iter())
}
}
struct DataCompositeMultiIndex<'a> {
pub name_age: MultiIndex<'a, (Vec<u8>, u32), Data, String>,
}
impl<'a> IndexList<Data> for DataCompositeMultiIndex<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<Data>> + '_> {
let v: Vec<&dyn Index<Data>> = vec![&self.name_age];
Box::new(v.into_iter())
}
}
const DATA: IndexedMap<&str, Data, DataIndexes> = IndexedMap::new(
"data",
DataIndexes {
name: MultiIndex::new(|_pk, d| d.name.clone(), "data", "data__name"),
age: UniqueIndex::new(|d| d.age, "data__age"),
name_lastname: UniqueIndex::new(
|d| index_string_tuple(&d.name, &d.last_name),
"data__name_lastname",
),
},
);
fn save_data<'a>(store: &mut MockStorage) -> (Vec<&'a str>, Vec<Data>) {
let mut pks = vec![];
let mut datas = vec![];
let data = Data {
name: "Maria".to_string(),
last_name: "Doe".to_string(),
age: 42,
};
let pk = "1";
DATA.save(store, pk, &data).unwrap();
pks.push(pk);
datas.push(data);
let data = Data {
name: "Maria".to_string(),
last_name: "Williams".to_string(),
age: 23,
};
let pk = "2";
DATA.save(store, pk, &data).unwrap();
pks.push(pk);
datas.push(data);
let data = Data {
name: "John".to_string(),
last_name: "Wayne".to_string(),
age: 32,
};
let pk = "3";
DATA.save(store, pk, &data).unwrap();
pks.push(pk);
datas.push(data);
let data = Data {
name: "Maria Luisa".to_string(),
last_name: "Rodriguez".to_string(),
age: 12,
};
let pk = "4";
DATA.save(store, pk, &data).unwrap();
pks.push(pk);
datas.push(data);
let data = Data {
name: "Marta".to_string(),
last_name: "After".to_string(),
age: 90,
};
let pk = "5";
DATA.save(store, pk, &data).unwrap();
pks.push(pk);
datas.push(data);
(pks, datas)
}
#[test]
fn store_and_load_by_index() {
let mut store = MockStorage::new();
let (pks, datas) = save_data(&mut store);
let pk = pks[0];
let data = &datas[0];
let loaded = DATA.load(&store, pk).unwrap();
assert_eq!(*data, loaded);
let count = DATA
.idx
.name
.prefix("Maria".to_string())
.range_raw(&store, None, None, Order::Ascending)
.count();
assert_eq!(2, count);
let marias: Vec<_> = DATA
.idx
.name
.prefix("Maria".to_string())
.range_raw(&store, None, None, Order::Ascending)
.collect::<StdResult<_>>()
.unwrap();
assert_eq!(2, marias.len());
let (k, v) = &marias[0];
assert_eq!(pk, String::from_slice(k).unwrap());
assert_eq!(data, v);
let count = DATA
.idx
.name
.prefix("Marib".to_string())
.range_raw(&store, None, None, Order::Ascending)
.count();
assert_eq!(0, count);
let count = DATA
.idx
.name
.prefix("Mari`".to_string())
.range_raw(&store, None, None, Order::Ascending)
.count();
assert_eq!(0, count);
let count = DATA
.idx
.name
.prefix("Maria5".to_string())
.range_raw(&store, None, None, Order::Ascending)
.count();
assert_eq!(0, count);
let key = ("Maria".to_string(), "".to_string());
let marias = DATA
.idx
.name
.range_raw(&store, Some(Bound::inclusive(key)), None, Order::Ascending)
.collect::<StdResult<Vec<_>>>()
.unwrap();
assert_eq!(4, marias.len());
let key = "Maria".to_string();
let marias2 = DATA
.idx
.name
.prefix_range_raw(
&store,
Some(PrefixBound::inclusive(key)),
None,
Order::Ascending,
)
.collect::<StdResult<Vec<_>>>()
.unwrap();
assert_eq!(4, marias2.len());
assert_eq!(marias, marias2);
let key = ("Maria".to_string(), "1".to_string());
let count = DATA
.idx
.name
.range_raw(&store, Some(Bound::exclusive(key)), None, Order::Ascending)
.count();
assert_eq!(3, count);
let age_key = 23u32;
let count = DATA
.idx
.age
.range_raw(
&store,
Some(Bound::inclusive(age_key)),
None,
Order::Ascending,
)
.count();
assert_eq!(4, count);
let proper = 42u32;
let aged = DATA.idx.age.item(&store, proper).unwrap().unwrap();
assert_eq!(pk, String::from_vec(aged.0).unwrap());
assert_eq!(*data, aged.1);
let too_old = 43u32;
let aged = DATA.idx.age.item(&store, too_old).unwrap();
assert_eq!(None, aged);
}
#[test]
fn existence() {
let mut store = MockStorage::new();
let (pks, _) = save_data(&mut store);
assert!(DATA.has(&store, pks[0]));
assert!(!DATA.has(&store, "6"));
}
#[test]
fn range_raw_simple_key_by_multi_index() {
let mut store = MockStorage::new();
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk = "5627";
DATA.save(&mut store, pk, &data1).unwrap();
let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk = "5628";
DATA.save(&mut store, pk, &data2).unwrap();
let data3 = Data {
name: "Maria".to_string(),
last_name: "Williams".to_string(),
age: 24,
};
let pk = "5629";
DATA.save(&mut store, pk, &data3).unwrap();
let data4 = Data {
name: "Maria Luisa".to_string(),
last_name: "Bemberg".to_string(),
age: 12,
};
let pk = "5630";
DATA.save(&mut store, pk, &data4).unwrap();
let marias: Vec<_> = DATA
.idx
.name
.prefix("Maria".to_string())
.range_raw(&store, None, None, Order::Descending)
.collect::<StdResult<_>>()
.unwrap();
let count = marias.len();
assert_eq!(2, count);
assert_eq!(marias[0].0, b"5629");
assert_eq!(marias[1].0, b"5627");
assert_eq!(marias[0].1, data3);
assert_eq!(marias[1].1, data1);
}
#[test]
fn range_simple_key_by_multi_index() {
let mut store = MockStorage::new();
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk = "5627";
DATA.save(&mut store, pk, &data1).unwrap();
let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk = "5628";
DATA.save(&mut store, pk, &data2).unwrap();
let data3 = Data {
name: "Maria".to_string(),
last_name: "Williams".to_string(),
age: 24,
};
let pk = "5629";
DATA.save(&mut store, pk, &data3).unwrap();
let data4 = Data {
name: "Maria Luisa".to_string(),
last_name: "Bemberg".to_string(),
age: 12,
};
let pk = "5630";
DATA.save(&mut store, pk, &data4).unwrap();
let marias: Vec<_> = DATA
.idx
.name
.prefix("Maria".to_string())
.range(&store, None, None, Order::Descending)
.collect::<StdResult<_>>()
.unwrap();
let count = marias.len();
assert_eq!(2, count);
assert_eq!(marias[0].0, "5629");
assert_eq!(marias[1].0, "5627");
assert_eq!(marias[0].1, data3);
assert_eq!(marias[1].1, data1);
}
#[test]
fn range_raw_composite_key_by_multi_index() {
let mut store = MockStorage::new();
let indexes = DataCompositeMultiIndex {
name_age: MultiIndex::new(
|_pk, d| index_tuple(&d.name, d.age),
"data",
"data__name_age",
),
};
let map = IndexedMap::new("data", indexes);
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk1: &[u8] = b"5627";
map.save(&mut store, pk1, &data1).unwrap();
let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk2: &[u8] = b"5628";
map.save(&mut store, pk2, &data2).unwrap();
let data3 = Data {
name: "Maria".to_string(),
last_name: "Young".to_string(),
age: 24,
};
let pk3: &[u8] = b"5629";
map.save(&mut store, pk3, &data3).unwrap();
let data4 = Data {
name: "Maria Luisa".to_string(),
last_name: "Bemberg".to_string(),
age: 43,
};
let pk4: &[u8] = b"5630";
map.save(&mut store, pk4, &data4).unwrap();
let marias: Vec<_> = map
.idx
.name_age
.sub_prefix(b"Maria".to_vec())
.range_raw(&store, None, None, Order::Descending)
.collect::<StdResult<_>>()
.unwrap();
let count = marias.len();
assert_eq!(2, count);
assert_eq!(pk1, marias[0].0);
assert_eq!(pk3, marias[1].0);
assert_eq!(data1, marias[0].1);
assert_eq!(data3, marias[1].1);
}
#[test]
fn range_composite_key_by_multi_index() {
let mut store = MockStorage::new();
let indexes = DataCompositeMultiIndex {
name_age: MultiIndex::new(
|_pk, d| index_tuple(&d.name, d.age),
"data",
"data__name_age",
),
};
let map = IndexedMap::new("data", indexes);
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk1 = "5627";
map.save(&mut store, pk1, &data1).unwrap();
let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk2 = "5628";
map.save(&mut store, pk2, &data2).unwrap();
let data3 = Data {
name: "Maria".to_string(),
last_name: "Young".to_string(),
age: 24,
};
let pk3 = "5629";
map.save(&mut store, pk3, &data3).unwrap();
let data4 = Data {
name: "Maria Luisa".to_string(),
last_name: "Bemberg".to_string(),
age: 43,
};
let pk4 = "5630";
map.save(&mut store, pk4, &data4).unwrap();
let marias: Vec<_> = map
.idx
.name_age
.sub_prefix(b"Maria".to_vec())
.range(&store, None, None, Order::Descending)
.collect::<StdResult<_>>()
.unwrap();
let count = marias.len();
assert_eq!(2, count);
assert_eq!(pk1, marias[0].0);
assert_eq!(pk3, marias[1].0);
assert_eq!(data1, marias[0].1);
assert_eq!(data3, marias[1].1);
}
#[test]
fn unique_index_enforced() {
let mut store = MockStorage::new();
let (pks, datas) = save_data(&mut store);
let data5 = Data {
name: "Marcel".to_string(),
last_name: "Laurens".to_string(),
age: 42,
};
let pk5 = "4";
DATA.save(&mut store, pk5, &data5).unwrap_err();
let age42 = 42u32;
let (k, v) = DATA.idx.age.item(&store, age42).unwrap().unwrap();
assert_eq!(String::from_vec(k).unwrap(), pks[0]);
assert_eq!(v.name, datas[0].name);
assert_eq!(v.age, datas[0].age);
let age23 = 23u32;
let (k, v) = DATA.idx.age.item(&store, age23).unwrap().unwrap();
assert_eq!(String::from_vec(k).unwrap(), pks[1]);
assert_eq!(v.name, datas[1].name);
assert_eq!(v.age, datas[1].age);
DATA.remove(&mut store, pks[0]).unwrap();
DATA.save(&mut store, pk5, &data5).unwrap();
let (k, v) = DATA.idx.age.item(&store, age42).unwrap().unwrap();
assert_eq!(String::from_vec(k).unwrap(), pk5);
assert_eq!(v.name, data5.name);
assert_eq!(v.age, data5.age);
}
#[test]
fn unique_index_enforced_composite_key() {
let mut store = MockStorage::new();
save_data(&mut store);
let data5 = Data {
name: "Maria".to_string(),
last_name: "Doe".to_string(),
age: 24,
};
let pk5 = "5";
DATA.save(&mut store, pk5, &data5).unwrap_err();
}
#[test]
fn remove_and_update_reflected_on_indexes() {
let mut store = MockStorage::new();
let name_count = |store: &MemoryStorage, name: &str| -> usize {
DATA.idx
.name
.prefix(name.to_string())
.keys_raw(store, None, None, Order::Ascending)
.count()
};
let (pks, _) = save_data(&mut store);
assert_eq!(name_count(&store, "Maria"), 2);
assert_eq!(name_count(&store, "John"), 1);
assert_eq!(name_count(&store, "Maria Luisa"), 1);
assert_eq!(name_count(&store, "Mary"), 0);
DATA.remove(&mut store, pks[1]).unwrap();
DATA.update(&mut store, pks[2], |d| -> StdResult<_> {
let mut x = d.unwrap();
assert_eq!(&x.name, "John");
x.name = "Mary".to_string();
Ok(x)
})
.unwrap();
assert_eq!(name_count(&store, "Maria"), 1);
assert_eq!(name_count(&store, "Maria Luisa"), 1);
assert_eq!(name_count(&store, "John"), 0);
assert_eq!(name_count(&store, "Mary"), 1);
}
#[test]
fn range_raw_simple_key_by_unique_index() {
let mut store = MockStorage::new();
let (pks, datas) = save_data(&mut store);
let res: StdResult<Vec<_>> = DATA
.idx
.age
.range_raw(&store, None, None, Order::Ascending)
.collect();
let ages = res.unwrap();
let count = ages.len();
assert_eq!(5, count);
assert_eq!(pks[3], String::from_slice(&ages[0].0).unwrap()); assert_eq!(pks[1], String::from_slice(&ages[1].0).unwrap()); assert_eq!(pks[2], String::from_slice(&ages[2].0).unwrap()); assert_eq!(pks[0], String::from_slice(&ages[3].0).unwrap()); assert_eq!(pks[4], String::from_slice(&ages[4].0).unwrap()); assert_eq!(datas[3], ages[0].1);
assert_eq!(datas[1], ages[1].1);
assert_eq!(datas[2], ages[2].1);
assert_eq!(datas[0], ages[3].1);
assert_eq!(datas[4], ages[4].1);
}
#[test]
fn range_simple_key_by_unique_index() {
let mut store = MockStorage::new();
let (pks, datas) = save_data(&mut store);
let res: StdResult<Vec<_>> = DATA
.idx
.age
.range(&store, None, None, Order::Ascending)
.collect();
let ages = res.unwrap();
let count = ages.len();
assert_eq!(5, count);
assert_eq!(pks[3], ages[0].0);
assert_eq!(pks[1], ages[1].0);
assert_eq!(pks[2], ages[2].0);
assert_eq!(pks[0], ages[3].0);
assert_eq!(pks[4], ages[4].0);
assert_eq!(datas[3], ages[0].1);
assert_eq!(datas[1], ages[1].1);
assert_eq!(datas[2], ages[2].1);
assert_eq!(datas[0], ages[3].1);
assert_eq!(datas[4], ages[4].1);
}
#[test]
fn range_raw_composite_key_by_unique_index() {
let mut store = MockStorage::new();
let (pks, datas) = save_data(&mut store);
let res: StdResult<Vec<_>> = DATA
.idx
.name_lastname
.prefix(b"Maria".to_vec())
.range_raw(&store, None, None, Order::Ascending)
.collect();
let marias = res.unwrap();
let count = marias.len();
assert_eq!(2, count);
assert_eq!(pks[0], String::from_slice(&marias[0].0).unwrap());
assert_eq!(pks[1], String::from_slice(&marias[1].0).unwrap());
assert_eq!(datas[0], marias[0].1);
assert_eq!(datas[1], marias[1].1);
}
#[test]
fn range_composite_key_by_unique_index() {
let mut store = MockStorage::new();
let (pks, datas) = save_data(&mut store);
let res: StdResult<Vec<_>> = DATA
.idx
.name_lastname
.prefix(b"Maria".to_vec())
.range(&store, None, None, Order::Ascending)
.collect();
let marias = res.unwrap();
let count = marias.len();
assert_eq!(2, count);
assert_eq!(pks[0], marias[0].0);
assert_eq!(pks[1], marias[1].0);
assert_eq!(datas[0], marias[0].1);
assert_eq!(datas[1], marias[1].1);
}
#[test]
#[cfg(feature = "iterator")]
fn range_simple_string_key() {
let mut store = MockStorage::new();
let (pks, datas) = save_data(&mut store);
let all: StdResult<Vec<_>> = DATA.range(&store, None, None, Order::Ascending).collect();
let all = all.unwrap();
assert_eq!(
all,
pks.clone()
.into_iter()
.map(str::to_string)
.zip(datas.clone().into_iter())
.collect::<Vec<_>>()
);
let all: StdResult<Vec<_>> = DATA
.range(&store, Some(Bound::inclusive("3")), None, Order::Ascending)
.collect();
let all = all.unwrap();
assert_eq!(
all,
pks.into_iter()
.map(str::to_string)
.zip(datas.into_iter())
.rev()
.take(3)
.rev()
.collect::<Vec<_>>()
);
}
#[test]
#[cfg(feature = "iterator")]
fn prefix_simple_string_key() {
let mut store = MockStorage::new();
let (pks, datas) = save_data(&mut store);
let all: StdResult<Vec<_>> = DATA
.prefix(())
.range(&store, None, None, Order::Ascending)
.collect();
let all = all.unwrap();
assert_eq!(
all,
pks.clone()
.into_iter()
.map(str::to_string)
.zip(datas.into_iter())
.collect::<Vec<_>>()
);
}
#[test]
#[cfg(feature = "iterator")]
fn prefix_composite_key() {
let mut store = MockStorage::new();
let indexes = DataCompositeMultiIndex {
name_age: MultiIndex::new(
|_pk, d| index_tuple(&d.name, d.age),
"data",
"data__name_age",
),
};
let map = IndexedMap::new("data", indexes);
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk1 = ("1", "5627");
map.save(&mut store, pk1, &data1).unwrap();
let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk2 = ("2", "5628");
map.save(&mut store, pk2, &data2).unwrap();
let data3 = Data {
name: "Maria".to_string(),
last_name: "Young".to_string(),
age: 24,
};
let pk3 = ("2", "5629");
map.save(&mut store, pk3, &data3).unwrap();
let data4 = Data {
name: "Maria Luisa".to_string(),
last_name: "Bemberg".to_string(),
age: 43,
};
let pk4 = ("3", "5630");
map.save(&mut store, pk4, &data4).unwrap();
let result: StdResult<Vec<_>> = map
.prefix("2")
.range(&store, None, None, Order::Ascending)
.collect();
let result = result.unwrap();
assert_eq!(
result,
[("5628".to_string(), data2), ("5629".to_string(), data3),]
);
}
#[test]
#[cfg(feature = "iterator")]
fn prefix_triple_key() {
let mut store = MockStorage::new();
let indexes = DataCompositeMultiIndex {
name_age: MultiIndex::new(
|_pk, d| index_tuple(&d.name, d.age),
"data",
"data__name_age",
),
};
let map = IndexedMap::new("data", indexes);
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk1 = ("1", "1", "5627");
map.save(&mut store, pk1, &data1).unwrap();
let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk2 = ("1", "2", "5628");
map.save(&mut store, pk2, &data2).unwrap();
let data3 = Data {
name: "Maria".to_string(),
last_name: "Young".to_string(),
age: 24,
};
let pk3 = ("2", "1", "5629");
map.save(&mut store, pk3, &data3).unwrap();
let data4 = Data {
name: "Maria Luisa".to_string(),
last_name: "Bemberg".to_string(),
age: 43,
};
let pk4 = ("2", "2", "5630");
map.save(&mut store, pk4, &data4).unwrap();
let result: StdResult<Vec<_>> = map
.prefix(("1", "2"))
.range(&store, None, None, Order::Ascending)
.collect();
let result = result.unwrap();
assert_eq!(result, [("5628".to_string(), data2),]);
}
#[test]
#[cfg(feature = "iterator")]
fn sub_prefix_triple_key() {
let mut store = MockStorage::new();
let indexes = DataCompositeMultiIndex {
name_age: MultiIndex::new(
|_pk, d| index_tuple(&d.name, d.age),
"data",
"data__name_age",
),
};
let map = IndexedMap::new("data", indexes);
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk1 = ("1", "1", "5627");
map.save(&mut store, pk1, &data1).unwrap();
let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk2 = ("1", "2", "5628");
map.save(&mut store, pk2, &data2).unwrap();
let data3 = Data {
name: "Maria".to_string(),
last_name: "Young".to_string(),
age: 24,
};
let pk3 = ("2", "1", "5629");
map.save(&mut store, pk3, &data3).unwrap();
let data4 = Data {
name: "Maria Luisa".to_string(),
last_name: "Bemberg".to_string(),
age: 43,
};
let pk4 = ("2", "2", "5630");
map.save(&mut store, pk4, &data4).unwrap();
let result: StdResult<Vec<_>> = map
.sub_prefix("1")
.range(&store, None, None, Order::Ascending)
.collect();
let result = result.unwrap();
assert_eq!(
result,
[
(("1".to_string(), "5627".to_string()), data1),
(("2".to_string(), "5628".to_string()), data2),
]
);
}
#[test]
#[cfg(feature = "iterator")]
fn prefix_range_simple_key() {
let mut store = MockStorage::new();
let indexes = DataCompositeMultiIndex {
name_age: MultiIndex::new(
|_pk, d| index_tuple(&d.name, d.age),
"data",
"data__name_age",
),
};
let map = IndexedMap::new("data", indexes);
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk1 = ("1", "5627");
map.save(&mut store, pk1, &data1).unwrap();
let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk2 = ("2", "5628");
map.save(&mut store, pk2, &data2).unwrap();
let data3 = Data {
name: "Maria".to_string(),
last_name: "Young".to_string(),
age: 24,
};
let pk3 = ("2", "5629");
map.save(&mut store, pk3, &data3).unwrap();
let data4 = Data {
name: "Maria Luisa".to_string(),
last_name: "Bemberg".to_string(),
age: 43,
};
let pk4 = ("3", "5630");
map.save(&mut store, pk4, &data4).unwrap();
let result: StdResult<Vec<_>> = map
.prefix_range(
&store,
Some(PrefixBound::inclusive("2")),
None,
Order::Ascending,
)
.collect();
let result = result.unwrap();
assert_eq!(
result,
[
(("2".to_string(), "5628".to_string()), data2.clone()),
(("2".to_string(), "5629".to_string()), data3.clone()),
(("3".to_string(), "5630".to_string()), data4)
]
);
let result: StdResult<Vec<_>> = map
.prefix_range(
&store,
Some(PrefixBound::inclusive("2")),
Some(PrefixBound::exclusive("3")),
Order::Ascending,
)
.collect();
let result = result.unwrap();
assert_eq!(
result,
[
(("2".to_string(), "5628".to_string()), data2),
(("2".to_string(), "5629".to_string()), data3),
]
);
}
#[test]
#[cfg(feature = "iterator")]
fn prefix_range_triple_key() {
let mut store = MockStorage::new();
let indexes = DataCompositeMultiIndex {
name_age: MultiIndex::new(
|_pk, d| index_tuple(&d.name, d.age),
"data",
"data__name_age",
),
};
let map = IndexedMap::new("data", indexes);
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk1 = ("1", "1", "5627");
map.save(&mut store, pk1, &data1).unwrap();
let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk2 = ("1", "2", "5628");
map.save(&mut store, pk2, &data2).unwrap();
let data3 = Data {
name: "Maria".to_string(),
last_name: "Young".to_string(),
age: 24,
};
let pk3 = ("2", "1", "5629");
map.save(&mut store, pk3, &data3).unwrap();
let data4 = Data {
name: "Maria Luisa".to_string(),
last_name: "Bemberg".to_string(),
age: 43,
};
let pk4 = ("2", "2", "5630");
map.save(&mut store, pk4, &data4).unwrap();
let result: StdResult<Vec<_>> = map
.prefix_range(
&store,
Some(PrefixBound::inclusive(("1", "2"))),
None,
Order::Ascending,
)
.collect();
let result = result.unwrap();
assert_eq!(
result,
[
(
("1".to_string(), "2".to_string(), "5628".to_string()),
data2.clone()
),
(
("2".to_string(), "1".to_string(), "5629".to_string()),
data3.clone()
),
(
("2".to_string(), "2".to_string(), "5630".to_string()),
data4
)
]
);
let result: StdResult<Vec<_>> = map
.prefix_range(
&store,
Some(PrefixBound::inclusive(("1", "2"))),
Some(PrefixBound::inclusive(("2", "1"))),
Order::Ascending,
)
.collect();
let result = result.unwrap();
assert_eq!(
result,
[
(
("1".to_string(), "2".to_string(), "5628".to_string()),
data2
),
(
("2".to_string(), "1".to_string(), "5629".to_string()),
data3
),
]
);
}
mod bounds_unique_index {
use super::*;
struct Indexes<'a> {
secondary: UniqueIndex<'a, u64, u64>,
}
impl<'a> IndexList<u64> for Indexes<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<u64>> + '_> {
let v: Vec<&dyn Index<u64>> = vec![&self.secondary];
Box::new(v.into_iter())
}
}
#[test]
#[cfg(feature = "iterator")]
fn composite_key_query() {
let indexes = Indexes {
secondary: UniqueIndex::new(|secondary| *secondary, "test_map__secondary"),
};
let map = IndexedMap::<&str, u64, Indexes>::new("test_map", indexes);
let mut store = MockStorage::new();
map.save(&mut store, "one", &1).unwrap();
map.save(&mut store, "two", &2).unwrap();
map.save(&mut store, "three", &3).unwrap();
let items: Vec<_> = map
.idx
.secondary
.range_raw(&store, None, Some(Bound::inclusive(1u64)), Order::Ascending)
.collect::<Result<_, _>>()
.unwrap();
let items: Vec<_> = items.into_iter().map(|(_, v)| v).collect();
assert_eq!(items, vec![1]);
let items: Vec<_> = map
.idx
.secondary
.range(&store, Some(Bound::exclusive(2u64)), None, Order::Ascending)
.collect::<Result<_, _>>()
.unwrap();
assert_eq!(items, vec![((), 3)]);
}
}
mod bounds_multi_index {
use super::*;
struct Indexes<'a> {
secondary: MultiIndex<'a, u64, u64, &'a str>,
}
impl<'a> IndexList<u64> for Indexes<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<u64>> + '_> {
let v: Vec<&dyn Index<u64>> = vec![&self.secondary];
Box::new(v.into_iter())
}
}
#[test]
#[cfg(feature = "iterator")]
fn composite_key_query() {
let indexes = Indexes {
secondary: MultiIndex::new(
|_pk, secondary| *secondary,
"test_map",
"test_map__secondary",
),
};
let map = IndexedMap::<&str, u64, Indexes>::new("test_map", indexes);
let mut store = MockStorage::new();
map.save(&mut store, "one", &1).unwrap();
map.save(&mut store, "two", &2).unwrap();
map.save(&mut store, "two2", &2).unwrap();
map.save(&mut store, "three", &3).unwrap();
let items: Vec<_> = map
.idx
.secondary
.prefix_range_raw(
&store,
None,
Some(PrefixBound::inclusive(1u64)),
Order::Ascending,
)
.collect::<Result<_, _>>()
.unwrap();
let items: Vec<_> = items.into_iter().map(|(_, v)| v).collect();
assert_eq!(items, vec![1]);
let items: Vec<_> = map
.idx
.secondary
.range(
&store,
Some(Bound::exclusive((2u64, "two"))),
None,
Order::Ascending,
)
.collect::<Result<_, _>>()
.unwrap();
assert_eq!(
items,
vec![("two2".to_string(), 2), ("three".to_string(), 3)]
);
}
}
mod pk_multi_index {
use super::*;
use cosmwasm_std::{Addr, Uint128};
struct Indexes<'a> {
spender: MultiIndex<'a, Addr, Uint128, (Addr, Addr)>,
}
impl<'a> IndexList<Uint128> for Indexes<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<Uint128>> + '_> {
let v: Vec<&dyn Index<Uint128>> = vec![&self.spender];
Box::new(v.into_iter())
}
}
#[test]
#[cfg(feature = "iterator")]
fn pk_based_index() {
fn pk_index(pk: &[u8]) -> Addr {
let (_owner, spender) = <(Addr, Addr)>::from_slice(pk).unwrap(); spender
}
let indexes = Indexes {
spender: MultiIndex::new(
|pk, _allow| pk_index(pk),
"allowances",
"allowances__spender",
),
};
let map = IndexedMap::<(&Addr, &Addr), Uint128, Indexes>::new("allowances", indexes);
let mut store = MockStorage::new();
map.save(
&mut store,
(&Addr::unchecked("owner1"), &Addr::unchecked("spender1")),
&Uint128::new(11),
)
.unwrap();
map.save(
&mut store,
(&Addr::unchecked("owner1"), &Addr::unchecked("spender2")),
&Uint128::new(12),
)
.unwrap();
map.save(
&mut store,
(&Addr::unchecked("owner2"), &Addr::unchecked("spender1")),
&Uint128::new(21),
)
.unwrap();
let items: Vec<_> = map
.range_raw(&store, None, None, Order::Ascending)
.collect::<Result<_, _>>()
.unwrap();
let items: Vec<_> = items.into_iter().map(|(_, v)| v.u128()).collect();
assert_eq!(items, vec![11, 12, 21]);
let items: Vec<_> = map
.idx
.spender
.range_raw(&store, None, None, Order::Ascending)
.collect::<Result<_, _>>()
.unwrap();
let items: Vec<_> = items.into_iter().map(|(_, v)| v.u128()).collect();
assert_eq!(items, vec![11, 21, 12]);
let items: Vec<_> = map
.prefix(&Addr::unchecked("owner1"))
.range_raw(&store, None, None, Order::Ascending)
.collect::<Result<_, _>>()
.unwrap();
let items: Vec<_> = items.into_iter().map(|(_, v)| v.u128()).collect();
assert_eq!(items, vec![11, 12]);
let items: Vec<_> = map
.idx
.spender
.prefix(Addr::unchecked("spender1"))
.range_raw(&store, None, None, Order::Ascending)
.collect::<Result<_, _>>()
.unwrap();
let items: Vec<_> = items.into_iter().map(|(_, v)| v.u128()).collect();
assert_eq!(items, vec![11, 21]);
let items: Vec<_> = map
.idx
.spender
.prefix(Addr::unchecked("spender2"))
.range(&store, None, None, Order::Ascending)
.collect::<Result<_, _>>()
.unwrap();
assert_eq!(
items,
vec![(
(Addr::unchecked("owner1"), Addr::unchecked("spender2")),
Uint128::new(12)
)]
);
}
}
#[test]
fn clear_works() {
let mut storage = MockStorage::new();
let (pks, _) = save_data(&mut storage);
DATA.clear(&mut storage);
for key in pks {
assert!(!DATA.has(&storage, key));
}
}
#[test]
fn is_empty_works() {
let mut storage = MockStorage::new();
assert!(DATA.is_empty(&storage));
save_data(&mut storage);
assert!(!DATA.is_empty(&storage));
}
}