cw_paginate/
lib.rs

1use cosmwasm_std::{Order, StdError, StdResult, Storage};
2use cw_storage_plus::{Bound, IndexList, IndexedMap, KeyDeserialize, Map, PrimaryKey};
3use serde::{de::DeserializeOwned, ser::Serialize};
4
5pub const DEFAULT_LIMIT: u32 = 10;
6pub const MAX_LIMIT: u32 = 30;
7
8/// Collect items in an iterator into a Vec.
9///
10/// For each item, apply a mutation as defined by `parse_fn`. This is useful if
11/// the contract wants to convert the {key, value} pairs into a response type.
12pub fn collect<'a, D, T, R, E, F>(
13    iter: Box<dyn Iterator<Item = StdResult<(D, T)>> + 'a>,
14    limit: Option<u32>,
15    parse_fn: F,
16) -> Result<Vec<R>, E>
17where
18    F: Fn(D, T) -> Result<R, E>,
19    E: From<StdError>,
20{
21    let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
22    iter.take(limit)
23        .map(|item| {
24            let (k, v) = item?;
25            parse_fn(k, v)
26        })
27        .collect()
28}
29
30/// Iterate entries in a `cw_storage_plus::Map`.
31///
32/// TODO: add docs
33pub fn paginate_map<'a, K, T, R, E, F>(
34    map: &Map<K, T>,
35    store: &dyn Storage,
36    start: Option<Bound<'a, K>>,
37    limit: Option<u32>,
38    parse_fn: F,
39) -> Result<Vec<R>, E>
40where
41    K: PrimaryKey<'a> + KeyDeserialize,
42    K::Output: 'static,
43    T: Serialize + DeserializeOwned,
44    F: Fn(K::Output, T) -> Result<R, E>,
45    E: From<StdError>,
46{
47    let iter = map.range(store, start, None, Order::Ascending);
48    collect(iter, limit, parse_fn)
49}
50
51/// Iterate entries in a `cw_storage_plus::Map` under a given prefix.
52///
53/// TODO: add docs
54pub fn paginate_map_prefix<'a, K, T, R, E, F>(
55    map: &Map<K, T>,
56    store: &dyn Storage,
57    prefix: K::Prefix,
58    start: Option<Bound<'a, K::Suffix>>,
59    limit: Option<u32>,
60    parse_fn: F,
61) -> Result<Vec<R>, E>
62where
63    K: PrimaryKey<'a>,
64    K::Suffix: PrimaryKey<'a> + KeyDeserialize,
65    <K::Suffix as KeyDeserialize>::Output: 'static,
66    T: Serialize + DeserializeOwned,
67    F: Fn(<K::Suffix as KeyDeserialize>::Output, T) -> Result<R, E>,
68    E: From<StdError>,
69{
70    let iter = map.prefix(prefix).range(store, start, None, Order::Ascending);
71    collect(iter, limit, parse_fn)
72}
73
74/// Iterate entries in a `cw_storage_plus::IndexedMap` under a given prefix.
75///
76/// TODO: add docs
77pub fn paginate_indexed_map<'a, K, T, I, R, E, F>(
78    map: &IndexedMap<K, T, I>,
79    store: &dyn Storage,
80    start: Option<Bound<'a, K>>,
81    limit: Option<u32>,
82    parse_fn: F,
83) -> Result<Vec<R>, E>
84where
85    K: PrimaryKey<'a> + KeyDeserialize,
86    K::Output: 'static,
87    T: Serialize + DeserializeOwned + Clone,
88    I: IndexList<T>,
89    F: Fn(K::Output, T) -> Result<R, E>,
90    E: From<StdError>,
91{
92    let iter = map.range(store, start, None, Order::Ascending);
93    collect(iter, limit, parse_fn)
94}
95
96// TODO: add unit tests