1use cosmwasm_schema::cw_serde;
48use cosmwasm_std::Order;
49use cw_storage_plus::{Bound, PrimaryKey};
50
51const DEFAULT_QUERY_LIMIT: u32 = 10;
52const MAX_QUERY_LIMIT: u32 = 100;
53
54#[cw_serde]
55pub enum QueryBound<T> {
56 Inclusive(T),
57 Exclusive(T),
58}
59
60#[cw_serde]
62pub struct QueryOptions<T> {
63 pub limit: Option<u32>,
65 pub descending: Option<bool>,
67 pub min: Option<QueryBound<T>>,
69 pub max: Option<QueryBound<T>>,
71}
72
73impl<T> Default for QueryOptions<T> {
74 fn default() -> Self {
75 QueryOptions {
76 descending: None,
77 min: None,
78 max: None,
79 limit: None,
80 }
81 }
82}
83
84pub struct QueryOptionsInternal<'a, U: PrimaryKey<'a>> {
87 pub limit: usize,
89 pub order: Order,
91 pub min: Option<Bound<'a, U>>,
93 pub max: Option<Bound<'a, U>>,
95}
96
97impl<T> QueryOptions<T> {
98 pub fn unpack<'a, U: PrimaryKey<'a>>(
99 &self,
100 offset_to_bound_fn: &dyn Fn(&T) -> U,
101 default_query_limit: Option<u32>,
102 max_query_limit: Option<u32>,
103 ) -> QueryOptionsInternal<'a, U> {
104 let default_query_limit = default_query_limit.unwrap_or(DEFAULT_QUERY_LIMIT);
105 let max_query_limit = max_query_limit.unwrap_or(MAX_QUERY_LIMIT);
106
107 let limit = self
108 .limit
109 .unwrap_or(default_query_limit)
110 .min(max_query_limit) as usize;
111
112 let mut order = Order::Ascending;
113 if let Some(_descending) = self.descending {
114 if _descending {
115 order = Order::Descending;
116 }
117 };
118
119 let min = match &self.min {
120 Some(QueryBound::Inclusive(offset)) => {
121 Some(Bound::inclusive(offset_to_bound_fn(offset)))
122 }
123 Some(QueryBound::Exclusive(offset)) => {
124 Some(Bound::exclusive(offset_to_bound_fn(offset)))
125 }
126 None => None,
127 };
128
129 let max = match &self.max {
130 Some(QueryBound::Inclusive(offset)) => {
131 Some(Bound::inclusive(offset_to_bound_fn(offset)))
132 }
133 Some(QueryBound::Exclusive(offset)) => {
134 Some(Bound::exclusive(offset_to_bound_fn(offset)))
135 }
136 None => None,
137 };
138
139 QueryOptionsInternal {
140 limit,
141 order,
142 min,
143 max,
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150
151 #[test]
152 fn try_unpack_default() {
153 use super::*;
154 let query_options = QueryOptions::<String>::default();
155
156 let query_options_internal =
157 query_options.unpack(&|offset: &String| offset.to_string(), None, None);
158
159 assert_eq!(query_options_internal.limit as u32, DEFAULT_QUERY_LIMIT);
160
161 match query_options_internal.order {
162 Order::Ascending => {}
163 Order::Descending => panic!("Expected Order::Ascending"),
164 }
165 assert!(query_options_internal.min.is_none());
166 assert!(query_options_internal.max.is_none());
167 }
168
169 #[test]
170 fn try_unpack_query_options() {
171 use super::*;
172 let query_options = QueryOptions {
173 descending: Some(true),
174 limit: Some(20),
175 min: Some(QueryBound::Inclusive("test".to_string())),
176 max: Some(QueryBound::Exclusive("test2".to_string())),
177 };
178
179 let query_options_internal =
180 query_options.unpack(&|offset: &String| offset.to_string(), None, None);
181
182 assert_eq!(query_options_internal.limit as u32, 20u32);
183 match query_options_internal.order {
184 Order::Ascending => panic!("Expected Order::Descending"),
185 Order::Descending => {}
186 }
187
188 match query_options_internal.min {
189 Some(Bound::Inclusive(offset)) => {
190 assert_eq!(offset.0, "test".to_string())
191 }
192 _ => panic!("Expected Bound::Inclusive"),
193 }
194
195 match query_options_internal.max {
196 Some(Bound::Exclusive(offset)) => {
197 assert_eq!(offset.0, "test2".to_string())
198 }
199 _ => panic!("Expected Bound::Exclusive"),
200 }
201 }
202}