#![cfg(feature = "iterator")]
use std::marker::PhantomData;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use cosmwasm_std::{from_slice, Binary, Order, Record, StdError, StdResult, Storage};
use crate::bound::PrefixBound;
use crate::de::KeyDeserialize;
use crate::iter_helpers::deserialize_kv;
use crate::map::Map;
use crate::prefix::namespaced_prefix_range;
use crate::{Bound, Index, Prefix, Prefixer, PrimaryKey};
#[derive(Deserialize, Serialize)]
pub(crate) struct UniqueRef<T> {
pk: Binary,
value: T,
}
pub struct UniqueIndex<'a, IK, T, PK = ()> {
index: fn(&T) -> IK,
idx_map: Map<'a, IK, UniqueRef<T>>,
idx_namespace: &'a [u8],
phantom: PhantomData<PK>,
}
impl<'a, IK, T, PK> UniqueIndex<'a, IK, T, PK> {
pub const fn new(idx_fn: fn(&T) -> IK, idx_namespace: &'a str) -> Self {
UniqueIndex {
index: idx_fn,
idx_map: Map::new(idx_namespace),
idx_namespace: idx_namespace.as_bytes(),
phantom: PhantomData,
}
}
}
impl<'a, IK, T, PK> Index<T> for UniqueIndex<'a, IK, T, PK>
where
T: Serialize + DeserializeOwned + Clone,
IK: PrimaryKey<'a>,
{
fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> {
let idx = (self.index)(data);
self.idx_map
.update(store, idx, |existing| -> StdResult<_> {
match existing {
Some(_) => Err(StdError::generic_err("Violates unique constraint on index")),
None => Ok(UniqueRef::<T> {
pk: pk.into(),
value: data.clone(),
}),
}
})?;
Ok(())
}
fn remove(&self, store: &mut dyn Storage, _pk: &[u8], old_data: &T) -> StdResult<()> {
let idx = (self.index)(old_data);
self.idx_map.remove(store, idx);
Ok(())
}
}
fn deserialize_unique_v<T: DeserializeOwned>(kv: Record) -> StdResult<Record<T>> {
let (_, v) = kv;
let t = from_slice::<UniqueRef<T>>(&v)?;
Ok((t.pk.0, t.value))
}
fn deserialize_unique_kv<K: KeyDeserialize, T: DeserializeOwned>(
kv: Record,
) -> StdResult<(K::Output, T)> {
let (_, v) = kv;
let t = from_slice::<UniqueRef<T>>(&v)?;
Ok((K::from_vec(t.pk.0)?, t.value))
}
impl<'a, IK, T, PK> UniqueIndex<'a, IK, T, PK>
where
T: Serialize + DeserializeOwned + Clone,
IK: PrimaryKey<'a>,
{
pub fn index_key(&self, k: IK) -> Vec<u8> {
k.joined_key()
}
fn no_prefix_raw(&self) -> Prefix<Vec<u8>, T, IK> {
Prefix::with_deserialization_functions(
self.idx_namespace,
&[],
&[],
|_, _, kv| deserialize_unique_v(kv),
|_, _, kv| deserialize_unique_v(kv),
)
}
pub fn item(&self, store: &dyn Storage, idx: IK) -> StdResult<Option<Record<T>>> {
let data = self
.idx_map
.may_load(store, idx)?
.map(|i| (i.pk.into(), i.value));
Ok(data)
}
}
impl<'a, IK, T, PK> UniqueIndex<'a, IK, T, PK>
where
T: Serialize + DeserializeOwned + Clone,
IK: PrimaryKey<'a>,
{
pub fn range_raw<'c>(
&self,
store: &'c dyn Storage,
min: Option<Bound<'a, IK>>,
max: Option<Bound<'a, IK>>,
order: Order,
) -> Box<dyn Iterator<Item = StdResult<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, IK>>,
max: Option<Bound<'a, IK>>,
order: Order,
) -> Box<dyn Iterator<Item = Vec<u8>> + 'c> {
self.no_prefix_raw().keys_raw(store, min, max, order)
}
}
#[cfg(feature = "iterator")]
impl<'a, IK, T, PK> UniqueIndex<'a, IK, T, PK>
where
PK: PrimaryKey<'a> + KeyDeserialize,
T: Serialize + DeserializeOwned + Clone,
IK: PrimaryKey<'a>,
{
pub fn prefix_range<'c>(
&self,
store: &'c dyn Storage,
min: Option<PrefixBound<'a, IK::Prefix>>,
max: Option<PrefixBound<'a, IK::Prefix>>,
order: cosmwasm_std::Order,
) -> Box<dyn Iterator<Item = StdResult<(PK::Output, T)>> + 'c>
where
T: 'c,
'a: 'c,
IK: 'c,
PK: 'c,
PK::Output: 'static,
{
let mapped = namespaced_prefix_range(store, self.idx_namespace, min, max, order)
.map(deserialize_kv::<PK, T>);
Box::new(mapped)
}
pub fn range<'c>(
&self,
store: &'c dyn Storage,
min: Option<Bound<'a, IK>>,
max: Option<Bound<'a, IK>>,
order: cosmwasm_std::Order,
) -> Box<dyn Iterator<Item = StdResult<(PK::Output, T)>> + 'c>
where
T: 'c,
PK::Output: 'static,
{
self.no_prefix().range(store, min, max, order)
}
pub fn keys<'c>(
&self,
store: &'c dyn Storage,
min: Option<Bound<'a, IK>>,
max: Option<Bound<'a, IK>>,
order: cosmwasm_std::Order,
) -> Box<dyn Iterator<Item = StdResult<PK::Output>> + 'c>
where
T: 'c,
PK::Output: 'static,
{
self.no_prefix().keys(store, min, max, order)
}
pub fn prefix(&self, p: IK::Prefix) -> Prefix<PK, T, IK::Suffix> {
Prefix::with_deserialization_functions(
self.idx_namespace,
&p.prefix(),
&[],
|_, _, kv| deserialize_unique_kv::<PK, _>(kv),
|_, _, kv| deserialize_unique_v(kv),
)
}
pub fn sub_prefix(&self, p: IK::SubPrefix) -> Prefix<PK, T, IK::SuperSuffix> {
Prefix::with_deserialization_functions(
self.idx_namespace,
&p.prefix(),
&[],
|_, _, kv| deserialize_unique_kv::<PK, _>(kv),
|_, _, kv| deserialize_unique_v(kv),
)
}
fn no_prefix(&self) -> Prefix<PK, T, IK> {
Prefix::with_deserialization_functions(
self.idx_namespace,
&[],
&[],
|_, _, kv| deserialize_unique_kv::<PK, _>(kv),
|_, _, kv| deserialize_unique_v(kv),
)
}
}