diff --git a/packages/storage-plus/README.md b/packages/storage-plus/README.md index 5dec524cb..8fe31abff 100644 --- a/packages/storage-plus/README.md +++ b/packages/storage-plus/README.md @@ -202,15 +202,14 @@ A `Map` key can be anything that implements the `PrimaryKey` trait. There are a - `impl<'a, T: Endian + Clone> PrimaryKey<'a> for IntKey` That means that byte and string slices, byte vectors, and strings, can be conveniently used as keys. -Moreover, some other types can be used as well, like addresses and addresses references, pairs and triples, and +Moreover, some other types can be used as well, like addresses and address references, pairs and triples, and integer types. -If the key represents and address, we suggest using `&Addr` for keys in storage, instead of `String` or string slices. This implies doing address validation -through `addr_validate` on any address passed in via a message, to ensure it's a legitimate address, and not random text -which will fail later. - -Thus, `pub fn addr_validate(&self, &str) -> Addr` in `deps.api` can be used for address validation, and the returned -`Addr` can be conveniently used as key in a `Map` or similar structure. +If the key represents an address, we suggest using `&Addr` for keys in storage, instead of `String` or string slices. +This implies doing address validation through `addr_validate` on any address passed in via a message, to ensure it's a +legitimate address, and not random text which will fail later. +`pub fn addr_validate(&self, &str) -> Addr` in `deps.api` can be used for address validation, and the returned `Addr` +can then be conveniently used as key in a `Map` or similar structure. ### Composite Keys @@ -265,7 +264,7 @@ reusing the calculated path to this key. For simple keys, this is just a bit less typing and a bit less gas if you use the same key for many calls. However, for composite keys, like `(b"owner", b"spender")` it is **much** less typing. And highly recommended anywhere -you will use the a composite key even twice: +you will use a composite key even twice: ```rust #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] @@ -410,15 +409,13 @@ fn demo() -> StdResult<()> { ## IndexedMap -In cw-plus, there's currently one example of `IndexedMap` usage, in the `cw721-base` contract. -Let's use it to illustrate `IndexedMap` definition and usage. +Let's sue one example of `IndexedMap` definition and usage, originally taken from the `cw721-base` contract. ### Definition ```rust pub struct TokenIndexes<'a> { - // pk goes to second tuple element - pub owner: MultiIndex<'a, (Addr, Vec), TokenInfo>, + pub owner: MultiIndex<'a, Addr, TokenInfo>, } impl<'a> IndexList for TokenIndexes<'a> { @@ -431,7 +428,7 @@ impl<'a> IndexList for TokenIndexes<'a> { pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { let indexes = TokenIndexes { owner: MultiIndex::new( - |d: &TokenInfo, k: Vec| (d.owner.clone(), k), + |d: &TokenInfo| d.owner.clone(), "tokens", "tokens__owner", ), @@ -443,20 +440,19 @@ pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { Let's discuss this piece by piece: ```rust pub struct TokenIndexes<'a> { - // pk goes to second tuple element - pub owner: MultiIndex<'a, (Addr, Vec), TokenInfo>, + pub owner: MultiIndex<'a, Addr, TokenInfo, String>, } ``` These are the index definitions. Here there's only one index, called `owner`. There could be more, as public members of the `TokenIndexes` struct. -We see that the `owner` index is a `MultiIndex`. A multi-index can have repeated values as keys. That's why -the primary key is being added as the last element of the multi-index key. +We see that the `owner` index is a `MultiIndex`. A multi-index can have repeated values as keys. The primary key is +used internally as the last element of the multi-index key, to disambiguate repeated index values. Like the name implies, this is an index over tokens, by owner. Given that an owner can have multiple tokens, we need a `MultiIndex` to be able to list / iterate over all the tokens a given owner has. -So, to recap, the `TokenInfo` data will originally be stored by `token_id` (which is a string value). +The `TokenInfo` data will originally be stored by `token_id` (which is a string value). You can see this in the token creation code: ```rust tokens().update(deps.storage, &msg.token_id, |old| match old { @@ -466,46 +462,26 @@ You can see this in the token creation code: ``` (Incidentally, this is using `update` instead of `save`, to avoid overwriting an already existing token). -Then, it will be indexed by token `owner` (which is an `Addr`), so that we can list all the tokens an owner has. -That's why the `owner` index key is `(Addr, Vec)`. The first owned element is the `owner` data -, whereas the second one is the `token_id` (converted internally to `Vec`). +Given that `token_id` is a string value, we specify `String` as the last argument of the `MultiIndex` definition. +That way, the deserialization of the primary key will be done to the right type (an owned string). -The important thing here is that the key (and its components, in the case of a combined key) must implement -the `PrimaryKey` trait. You can see that the 2-tuple `(_, _)`, `Addr`, and `Vec` do implement `PrimaryKey`: +Then, this `TokenInfo` data will be indexed by token `owner` (which is an `Addr`). So that we can list all the tokens +an owner has. That's why the `owner` index key is `Addr`. -```rust -impl<'a, T: PrimaryKey<'a> + Prefixer<'a>, U: PrimaryKey<'a>> PrimaryKey<'a> for (T, U) { - type Prefix = T; - type SubPrefix = (); - - fn key(&self) -> Vec<&[u8]> { - let mut keys = self.0.key(); - keys.extend(&self.1.key()); - keys - } -} -``` +Other important thing here is that the key (and its components, in the case of a composite key) must implement +the `PrimaryKey` trait. You can see that `Addr` do implement `PrimaryKey`: ```rust impl<'a> PrimaryKey<'a> for Addr { - type Prefix = (); - type SubPrefix = (); - - fn key(&self) -> Vec<&[u8]> { - // this is simple, we don't add more prefixes - vec![self.as_bytes()] - } -} -``` - -```rust -impl<'a> PrimaryKey<'a> for Vec { - type Prefix = (); - type SubPrefix = (); - - fn key(&self) -> Vec<&[u8]> { - vec![&self] - } + type Prefix = (); + type SubPrefix = (); + type Suffix = Self; + type SuperSuffix = Self; + + fn key(&self) -> Vec { + // this is simple, we don't add more prefixes + vec![Key::Ref(self.as_bytes())] + } } ``` @@ -525,7 +501,7 @@ impl<'a> IndexList for TokenIndexes<'a> { This implements the `IndexList` trait for `TokenIndexes`. Note: this code is more or less boiler-plate, and needed for the internals. Do not try to customize this; just return a list of all indexes. -Implementing this trait serves two purposes (which are really one, and the same): it allows the indexes +Implementing this trait serves two purposes (which are really one and the same): it allows the indexes to be queried through `get_indexes`, and, it allows `TokenIndexes` to be treated as an `IndexList`. So that it can be passed as a parameter during `IndexedMap` construction, below: @@ -533,7 +509,7 @@ it can be passed as a parameter during `IndexedMap` construction, below: pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { let indexes = TokenIndexes { owner: MultiIndex::new( - |d: &TokenInfo, k: Vec| (d.owner.clone(), k), + |d: &TokenInfo| d.owner.clone(), "tokens", "tokens__owner", ), @@ -543,17 +519,15 @@ pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { ``` Here `tokens()` is just a helper function, that simplifies the `IndexedMap` construction for us. First the -index (es) is (are) created, and then, the `IndexedMap` is created (using `IndexedMap::new`), and returned. +index (es) is (are) created, and then, the `IndexedMap` is created and returned. During index creation, we must supply an index function per index ```rust - owner: MultiIndex::new( - |d: &TokenInfo, k: Vec| (d.owner.clone(), k), + owner: MultiIndex::new(|d: &TokenInfo| d.owner.clone(), ``` -, which is the one that will take the value, and the primary key (which is always in `Vec` form) of the -original map, and create the index key from them. Of course, this requires that the elements required -for the index key are present in the value (which makes sense). +, which is the one that will take the value of the original map, and create the index key from it. +Of course, this requires that the elements required for the index key are present in the value. Besides the index function, we must also supply the namespace of the pk, and the one for the new index. --- @@ -595,7 +569,9 @@ Notice this uses `prefix()`, explained above in the `Map` section. let tokens = res?; ``` Now `tokens` contains `(token_id, TokenInfo)` pairs for the given `owner`. -The pk values are `Vec`, as this is a limitation of the current implementation. +The pk values are `Vec` in the case of `prefix` + `range`, but will be deserialized to the proper type using +`prefix_de` + `range_de`; provided that the (optional) pk deserialization type (`String`, in this case) +is specified in the `MultiIndex` definition (see #Index keys deserialization, below). Another example that is similar, but returning only the `token_id`s, using the `keys()` method: ```rust @@ -612,22 +588,13 @@ Another example that is similar, but returning only the `token_id`s, using the ` .take(limit) .collect(); ``` -Now `pks` contains `token_id` values (as `Vec`s) for the given `owner`. +Now `pks` contains `token_id` values (as raw `Vec`s) for the given `owner`. Again, by using `prefix_de` + `range_de`, +a deserialized key can be obtained instead, as detailed in the next section. ### Index keys deserialization -To deserialize keys of indexes (using the `*_de` functions), there are currently some requirements / limitations: - -- For `UniqueIndex`: The primary key (`PK`) type needs to be specified, in order to deserialize the primary key to it. -This generic type comes with a default of `()`, which means that no deserialization / data will be provided -for the primary key. This is for backwards compatibility with the current `UniqueIndex` impl. It can also come handy -in cases you don't need the primary key, and are interested only in the deserialized value. - -- For `MultiIndex`: The last element of the index tuple must be specified with the type you want it to be deserialized. -That is, the last tuple element serves as a marker for the deserialization type (in the same way `PK` does it in -`UniqueIndex`). - -- There are currently some inconsistencies in the values that are returned for the different index keys. `MultiIndex` -returns a tuple with the remaining part of the index key along with the primary key, whereas `UniqueIndex` returns only -the primary key. This will be changed in the future (See https://github.com/CosmWasm/cw-plus/issues/532) for consistency -and compatibility with the base `Map` type behaviour. +For `UniqueIndex` and `MultiIndex`, the primary key (`PK`) type needs to be specified, in order to deserialize +the primary key to it. This generic type comes with a default of `()`, which means that no deserialization / data +will be provided for the primary key. This is for backwards compatibility with the current `UniqueIndex` / `MultiIndex` +impls. It can also come in handy in cases you don't need the primary key, and are interested only in the deserialized +values. diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 022a45048..580182a31 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -281,8 +281,8 @@ where mod test { use super::*; - use crate::indexes::{index_string_tuple, index_triple}; - use crate::{MultiIndex, UniqueIndex}; + use crate::indexes::index_string_tuple; + use crate::{index_tuple, MultiIndex, UniqueIndex}; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::{MemoryStorage, Order}; use serde::{Deserialize, Serialize}; @@ -295,8 +295,8 @@ mod test { } struct DataIndexes<'a> { - // Second arg is for storing pk - pub name: MultiIndex<'a, (String, String), Data, String>, + // Last type parameters are for signaling pk deserialization + pub name: MultiIndex<'a, String, Data, String>, pub age: UniqueIndex<'a, u32, Data, String>, pub name_lastname: UniqueIndex<'a, (Vec, Vec), Data, String>, } @@ -311,8 +311,8 @@ mod test { // For composite multi index tests struct DataCompositeMultiIndex<'a> { - // Third arg needed for storing pk - pub name_age: MultiIndex<'a, (Vec, u32, Vec), Data, String>, + // Last type parameter is for signaling pk deserialization + pub name_age: MultiIndex<'a, (Vec, u32), Data, String>, } // Future Note: this can likely be macro-derived @@ -326,11 +326,7 @@ mod test { // Can we make it easier to define this? (less wordy generic) fn build_map<'a>() -> IndexedMap<'a, &'a str, Data, DataIndexes<'a>> { let indexes = DataIndexes { - name: MultiIndex::new( - |d, k| (d.name.clone(), unsafe { String::from_utf8_unchecked(k) }), - "data", - "data__name", - ), + name: MultiIndex::new(|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), @@ -423,10 +419,7 @@ mod test { .count(); assert_eq!(2, count); - // TODO: we load by wrong keys - get full storage key! - - // load it by secondary index (we must know how to compute this) - // let marias: Vec<_>> = map + // load it by secondary index let marias: Vec<_> = map .idx .name @@ -469,8 +462,8 @@ mod test { // index_key() over MultiIndex works (empty pk) // In a MultiIndex, an index key is composed by the index and the primary key. // Primary key may be empty (so that to iterate over all elements that match just the index) - let key = ("Maria".to_string(), "".to_string()); - // Use the index_key() helper to build the (raw) index key + let key = "Maria".to_string(); + // Use the index_key() helper to build the (raw) index key with an empty pk let key = map.idx.name.index_key(key); // Iterate using a bound over the raw key let count = map @@ -484,8 +477,8 @@ mod test { // index_key() over MultiIndex works (non-empty pk) // Build key including a non-empty pk let key = ("Maria".to_string(), "1".to_string()); - // Use the index_key() helper to build the (raw) index key - let key = map.idx.name.index_key(key); + // Use the joined_key() helper to build the (raw) index key + let key = key.joined_key(); // Iterate using a (exclusive) bound over the raw key. // (Useful for pagination / continuation contexts). let count = map @@ -643,11 +636,7 @@ mod test { let mut store = MockStorage::new(); let indexes = DataCompositeMultiIndex { - name_age: MultiIndex::new( - |d, k| index_triple(&d.name, d.age, k), - "data", - "data__name_age", - ), + name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), }; let map = IndexedMap::new("data", indexes); @@ -708,11 +697,7 @@ mod test { let mut store = MockStorage::new(); let indexes = DataCompositeMultiIndex { - name_age: MultiIndex::new( - |d, k| index_triple(&d.name, d.age, k), - "data", - "data__name_age", - ), + name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), }; let map = IndexedMap::new("data", indexes); @@ -1075,11 +1060,7 @@ mod test { let mut store = MockStorage::new(); let indexes = DataCompositeMultiIndex { - name_age: MultiIndex::new( - |d, k| index_triple(&d.name, d.age, k), - "data", - "data__name_age", - ), + name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), }; let map = IndexedMap::new("data", indexes); @@ -1134,11 +1115,7 @@ mod test { let mut store = MockStorage::new(); let indexes = DataCompositeMultiIndex { - name_age: MultiIndex::new( - |d, k| index_triple(&d.name, d.age, k), - "data", - "data__name_age", - ), + name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), }; let map = IndexedMap::new("data", indexes); @@ -1190,11 +1167,7 @@ mod test { let mut store = MockStorage::new(); let indexes = DataCompositeMultiIndex { - name_age: MultiIndex::new( - |d, k| index_triple(&d.name, d.age, k), - "data", - "data__name_age", - ), + name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), }; let map = IndexedMap::new("data", indexes); @@ -1252,11 +1225,7 @@ mod test { let mut store = MockStorage::new(); let indexes = DataCompositeMultiIndex { - name_age: MultiIndex::new( - |d, k| index_triple(&d.name, d.age, k), - "data", - "data__name_age", - ), + name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), }; let map = IndexedMap::new("data", indexes); @@ -1337,11 +1306,7 @@ mod test { let mut store = MockStorage::new(); let indexes = DataCompositeMultiIndex { - name_age: MultiIndex::new( - |d, k| index_triple(&d.name, d.age, k), - "data", - "data__name_age", - ), + name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), }; let map = IndexedMap::new("data", indexes); @@ -1435,7 +1400,7 @@ mod test { use super::*; struct Indexes<'a> { - secondary: MultiIndex<'a, (u64, Vec), u64>, + secondary: MultiIndex<'a, u64, u64>, } impl<'a> IndexList for Indexes<'a> { @@ -1450,7 +1415,7 @@ mod test { fn composite_key_query() { let indexes = Indexes { secondary: MultiIndex::new( - |secondary, k| (*secondary, k), + |secondary| *secondary, "test_map", "test_map__secondary", ), diff --git a/packages/storage-plus/src/indexed_snapshot.rs b/packages/storage-plus/src/indexed_snapshot.rs index 329218aeb..8b4470927 100644 --- a/packages/storage-plus/src/indexed_snapshot.rs +++ b/packages/storage-plus/src/indexed_snapshot.rs @@ -297,8 +297,8 @@ where mod test { use super::*; - use crate::indexes::{index_string_tuple, index_triple}; - use crate::{Index, MultiIndex, UniqueIndex}; + use crate::indexes::index_string_tuple; + use crate::{index_tuple, Index, MultiIndex, UniqueIndex}; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::{MemoryStorage, Order}; use serde::{Deserialize, Serialize}; @@ -311,11 +311,9 @@ mod test { } struct DataIndexes<'a> { - // Second arg is for storing pk - pub name: MultiIndex<'a, (Vec, String), Data, String>, - // Last generic type arg is pk deserialization type + // Last type parameters are for signaling pk deserialization + pub name: MultiIndex<'a, Vec, Data, String>, pub age: UniqueIndex<'a, u32, Data, String>, - // Last generic type arg is pk deserialization type pub name_lastname: UniqueIndex<'a, (Vec, Vec), Data, String>, } @@ -329,8 +327,8 @@ mod test { // For composite multi index tests struct DataCompositeMultiIndex<'a> { - // Third arg needed for storing pk - pub name_age: MultiIndex<'a, (Vec, u32, Vec), Data, String>, + // Last type parameter is for signaling pk deserialization + pub name_age: MultiIndex<'a, (Vec, u32), Data, String>, } // Future Note: this can likely be macro-derived @@ -344,15 +342,7 @@ mod test { // Can we make it easier to define this? (less wordy generic) fn build_snapshot_map<'a>() -> IndexedSnapshotMap<'a, &'a str, Data, DataIndexes<'a>> { let indexes = DataIndexes { - name: MultiIndex::new( - |d, k| { - (d.name.as_bytes().to_vec(), unsafe { - String::from_utf8_unchecked(k) - }) - }, - "data", - "data__name", - ), + name: MultiIndex::new(|d| d.name.as_bytes().to_vec(), "data", "data__name"), age: UniqueIndex::new(|d| d.age, "data__age"), name_lastname: UniqueIndex::new( |d| index_string_tuple(&d.name, &d.last_name), @@ -445,10 +435,7 @@ mod test { .count(); assert_eq!(2, count); - // TODO: we load by wrong keys - get full storage key! - - // load it by secondary index (we must know how to compute this) - // let marias: Vec<_>> = map + // load it by secondary index let marias: Vec<_> = map .idx .name @@ -626,11 +613,7 @@ mod test { let mut height = 2; let indexes = DataCompositeMultiIndex { - name_age: MultiIndex::new( - |d, k| index_triple(&d.name, d.age, k), - "data", - "data__name_age", - ), + name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), }; let map = IndexedSnapshotMap::new("data", "checks", "changes", Strategy::EveryBlock, indexes); @@ -696,11 +679,7 @@ mod test { let mut height = 2; let indexes = DataCompositeMultiIndex { - name_age: MultiIndex::new( - |d, k| index_triple(&d.name, d.age, k), - "data", - "data__name_age", - ), + name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), }; let map = IndexedSnapshotMap::new("data", "checks", "changes", Strategy::EveryBlock, indexes); @@ -1096,11 +1075,7 @@ mod test { let mut store = MockStorage::new(); let indexes = DataCompositeMultiIndex { - name_age: MultiIndex::new( - |d, k| index_triple(&d.name, d.age, k), - "data", - "data__name_age", - ), + name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), }; let map = IndexedSnapshotMap::new("data", "checks", "changes", Strategy::EveryBlock, indexes); diff --git a/packages/storage-plus/src/indexes/multi.rs b/packages/storage-plus/src/indexes/multi.rs index e014de58f..4b8bd5c8e 100644 --- a/packages/storage-plus/src/indexes/multi.rs +++ b/packages/storage-plus/src/indexes/multi.rs @@ -22,26 +22,24 @@ use std::marker::PhantomData; /// The stored pk_len is used to recover the pk from the index namespace, and perform /// the secondary load of the associated value from the main map. /// -/// The MultiIndex definition must include a field for the pk. That is, the MultiIndex K value -/// is always a n-tuple (n >= 2) and its last element must be the pk. -/// The index function must therefore put the pk as last element, when generating the index. -/// The optional PK type defines the type of Primary Key deserialization. -pub struct MultiIndex<'a, K, T, PK = ()> { - index: fn(&T, Vec) -> K, +/// The (optional) PK type defines the type of Primary Key deserialization. +pub struct MultiIndex<'a, IK, T, PK = ()> { + index: fn(&T) -> IK, idx_namespace: &'a [u8], - idx_map: Map<'a, K, u32>, + // note, we collapse the ik - combining everything under the namespace - and concatenating the pk + idx_map: Map<'a, Vec, u32>, pk_namespace: &'a [u8], phantom: PhantomData, } -impl<'a, K, T, PK> MultiIndex<'a, K, T, PK> +impl<'a, IK, T, PK> MultiIndex<'a, IK, T, PK> where T: Serialize + DeserializeOwned + Clone, { // TODO: make this a const fn /// Create a new MultiIndex /// - /// idx_fn - lambda creating index key from value (first argument) and primary key (second argument) + /// idx_fn - lambda creating index key from value /// pk_namespace - prefix for the primary key /// idx_namespace - prefix for the index value /// @@ -58,16 +56,12 @@ where /// } /// /// let index: MultiIndex<_, _, String> = MultiIndex::new( - /// |d: &Data, k: Vec| (d.age, k), + /// |d: &Data| d.age, /// "age", /// "age__owner", /// ); /// ``` - pub fn new( - idx_fn: fn(&T, Vec) -> K, - pk_namespace: &'a str, - idx_namespace: &'a str, - ) -> Self { + pub fn new(idx_fn: fn(&T) -> IK, pk_namespace: &'a str, idx_namespace: &'a str) -> Self { MultiIndex { index: idx_fn, idx_namespace: idx_namespace.as_bytes(), @@ -127,29 +121,29 @@ fn deserialize_multi_kv( Ok((K::from_slice(pk)?, v)) } -impl<'a, K, T, PK> Index for MultiIndex<'a, K, T, PK> +impl<'a, IK, T, PK> Index for MultiIndex<'a, IK, T, PK> where T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, + IK: PrimaryKey<'a>, { fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> { - let idx = (self.index)(data, pk.to_vec()); + let idx = (self.index)(data).joined_extra_key(pk); self.idx_map.save(store, idx, &(pk.len() as u32)) } fn remove(&self, store: &mut dyn Storage, pk: &[u8], old_data: &T) -> StdResult<()> { - let idx = (self.index)(old_data, pk.to_vec()); + let idx = (self.index)(old_data).joined_extra_key(pk); self.idx_map.remove(store, idx); Ok(()) } } -impl<'a, K, T, PK> MultiIndex<'a, K, T, PK> +impl<'a, IK, T, PK> MultiIndex<'a, IK, T, PK> where T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, + IK: PrimaryKey<'a> + Prefixer<'a>, { - pub fn prefix(&self, p: K::Prefix) -> Prefix, T> { + pub fn prefix(&self, p: IK) -> Prefix, T> { Prefix::with_deserialization_function( self.idx_namespace, &p.prefix(), @@ -158,7 +152,7 @@ where ) } - pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix, T> { + pub fn sub_prefix(&self, p: IK::Prefix) -> Prefix, T> { Prefix::with_deserialization_function( self.idx_namespace, &p.prefix(), @@ -176,18 +170,18 @@ where ) } - pub fn index_key(&self, k: K) -> Vec { - k.joined_key() + pub fn index_key(&self, k: IK) -> Vec { + k.joined_extra_key(b"") } #[cfg(test)] - pub fn count(&self, store: &dyn Storage, p: K::Prefix) -> usize { + pub fn count(&self, store: &dyn Storage, p: IK) -> usize { let prefix = self.prefix(p); prefix.keys(store, None, None, Order::Ascending).count() } #[cfg(test)] - pub fn all_pks(&self, store: &dyn Storage, p: K::Prefix) -> Vec> { + pub fn all_pks(&self, store: &dyn Storage, p: IK) -> Vec> { let prefix = self.prefix(p); prefix .keys(store, None, None, Order::Ascending) @@ -195,17 +189,17 @@ where } #[cfg(test)] - pub fn all_items(&self, store: &dyn Storage, p: K::Prefix) -> StdResult>> { + pub fn all_items(&self, store: &dyn Storage, p: IK) -> StdResult>> { let prefix = self.prefix(p); prefix.range(store, None, None, Order::Ascending).collect() } } // short-cut for simple keys, rather than .prefix(()).range(...) -impl<'a, K, T, PK> MultiIndex<'a, K, T, PK> +impl<'a, IK, T, PK> MultiIndex<'a, IK, T, PK> where T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, + IK: PrimaryKey<'a> + Prefixer<'a>, { // I would prefer not to copy code from Prefix, but no other way // with lifetimes (create Prefix inside function and return ref = no no) @@ -241,8 +235,8 @@ where pub fn prefix_range<'c>( &'c self, store: &'c dyn Storage, - min: Option>, - max: Option>, + min: Option>, + max: Option>, order: cosmwasm_std::Order, ) -> Box>> + 'c> where @@ -256,13 +250,13 @@ where } #[cfg(feature = "iterator")] -impl<'a, K, T, PK> MultiIndex<'a, K, T, PK> +impl<'a, IK, T, PK> MultiIndex<'a, IK, T, PK> where PK: PrimaryKey<'a> + KeyDeserialize, T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, + IK: PrimaryKey<'a> + Prefixer<'a>, { - pub fn prefix_de(&self, p: K::Prefix) -> Prefix { + pub fn prefix_de(&self, p: IK) -> Prefix { Prefix::with_deserialization_function( self.idx_namespace, &p.prefix(), @@ -271,7 +265,7 @@ where ) } - pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { + pub fn sub_prefix_de(&self, p: IK::Prefix) -> Prefix { Prefix::with_deserialization_function( self.idx_namespace, &p.prefix(), @@ -282,11 +276,11 @@ where } #[cfg(feature = "iterator")] -impl<'a, K, T, PK> MultiIndex<'a, K, T, PK> +impl<'a, IK, T, PK> MultiIndex<'a, IK, T, PK> where PK: PrimaryKey<'a> + KeyDeserialize, T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a> + KeyDeserialize, + IK: PrimaryKey<'a> + KeyDeserialize + Prefixer<'a>, { /// While `range_de` over a `prefix_de` fixes the prefix to one element and iterates over the /// remaining, `prefix_range_de` accepts bounds for the lowest and highest elements of the @@ -297,14 +291,14 @@ where pub fn prefix_range_de<'c>( &self, store: &'c dyn Storage, - min: Option>, - max: Option>, + min: Option>, + max: Option>, order: cosmwasm_std::Order, ) -> Box> + 'c> where T: 'c, 'a: 'c, - K: 'c, + IK: 'c, PK: 'c, PK::Output: 'static, { diff --git a/packages/storage-plus/src/indexes/unique.rs b/packages/storage-plus/src/indexes/unique.rs index fba350b94..13d033475 100644 --- a/packages/storage-plus/src/indexes/unique.rs +++ b/packages/storage-plus/src/indexes/unique.rs @@ -25,14 +25,14 @@ pub(crate) struct UniqueRef { /// UniqueIndex stores (namespace, index_name, idx_value) -> {key, value} /// Allows one value per index (i.e. unique) and copies pk and data /// The optional PK type defines the type of Primary Key deserialization. -pub struct UniqueIndex<'a, K, T, PK = ()> { - index: fn(&T) -> K, - idx_map: Map<'a, K, UniqueRef>, +pub struct UniqueIndex<'a, IK, T, PK = ()> { + index: fn(&T) -> IK, + idx_map: Map<'a, IK, UniqueRef>, idx_namespace: &'a [u8], phantom: PhantomData, } -impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> { +impl<'a, IK, T, PK> UniqueIndex<'a, IK, T, PK> { // TODO: make this a const fn /// Create a new UniqueIndex /// @@ -51,7 +51,7 @@ impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> { /// /// UniqueIndex::<_, _, ()>::new(|d: &Data| d.age, "data__age"); /// ``` - pub fn new(idx_fn: fn(&T) -> K, idx_namespace: &'a str) -> Self { + pub fn new(idx_fn: fn(&T) -> IK, idx_namespace: &'a str) -> Self { UniqueIndex { index: idx_fn, idx_map: Map::new(idx_namespace), @@ -61,10 +61,10 @@ impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> { } } -impl<'a, K, T, PK> Index for UniqueIndex<'a, K, T, PK> +impl<'a, IK, T, PK> Index for UniqueIndex<'a, IK, T, PK> where T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, + IK: PrimaryKey<'a>, { fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> { let idx = (self.index)(data); @@ -103,22 +103,22 @@ fn deserialize_unique_kv( Ok((K::from_vec(t.pk.0)?, t.value)) } -impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> +impl<'a, IK, T, PK> UniqueIndex<'a, IK, T, PK> where T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, + IK: PrimaryKey<'a>, { - pub fn index_key(&self, k: K) -> Vec { + pub fn index_key(&self, k: IK) -> Vec { k.joined_key() } - pub fn prefix(&self, p: K::Prefix) -> Prefix, T> { + pub fn prefix(&self, p: IK::Prefix) -> Prefix, T> { Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { deserialize_unique_v(kv) }) } - pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix, T> { + pub fn sub_prefix(&self, p: IK::SubPrefix) -> Prefix, T> { Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { deserialize_unique_v(kv) }) @@ -131,7 +131,7 @@ where } /// returns all items that match this secondary index, always by pk Ascending - pub fn item(&self, store: &dyn Storage, idx: K) -> StdResult>> { + pub fn item(&self, store: &dyn Storage, idx: IK) -> StdResult>> { let data = self .idx_map .may_load(store, idx)? @@ -141,10 +141,10 @@ where } // short-cut for simple keys, rather than .prefix(()).range(...) -impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> +impl<'a, IK, T, PK> UniqueIndex<'a, IK, T, PK> where T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, + IK: PrimaryKey<'a>, { // I would prefer not to copy code from Prefix, but no other way // with lifetimes (create Prefix inside function and return ref = no no) @@ -173,11 +173,11 @@ where } #[cfg(feature = "iterator")] -impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> +impl<'a, IK, T, PK> UniqueIndex<'a, IK, T, PK> where PK: PrimaryKey<'a> + KeyDeserialize, T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, + IK: PrimaryKey<'a>, { /// While `range_de` over a `prefix_de` fixes the prefix to one element and iterates over the /// remaining, `prefix_range_de` accepts bounds for the lowest and highest elements of the @@ -188,14 +188,14 @@ where pub fn prefix_range_de<'c>( &self, store: &'c dyn Storage, - min: Option>, - max: Option>, + min: Option>, + max: Option>, order: cosmwasm_std::Order, ) -> Box> + 'c> where T: 'c, 'a: 'c, - K: 'c, + IK: 'c, PK: 'c, PK::Output: 'static, { @@ -232,13 +232,13 @@ where self.no_prefix_de().keys_de(store, min, max, order) } - pub fn prefix_de(&self, p: K::Prefix) -> Prefix { + pub fn prefix_de(&self, p: IK::Prefix) -> Prefix { Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { deserialize_unique_kv::(kv) }) } - pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { + pub fn sub_prefix_de(&self, p: IK::SubPrefix) -> Prefix { Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { deserialize_unique_kv::(kv) }) diff --git a/packages/storage-plus/src/keys.rs b/packages/storage-plus/src/keys.rs index 32eb8a005..15b7a6c19 100644 --- a/packages/storage-plus/src/keys.rs +++ b/packages/storage-plus/src/keys.rs @@ -76,6 +76,11 @@ pub trait PrimaryKey<'a>: Clone { keys[l - 1].as_ref(), ) } + + fn joined_extra_key(&self, key: &[u8]) -> Vec { + let keys = self.key(); + namespaces_with_key(&keys.iter().map(Key::as_ref).collect::>(), key) + } } // Empty / no primary key