1use crate::regex::build_regex;
2use crate::types::core::Key;
3use crate::types::list::{
4 ListMatcher, ListOrder, ListOrderField, ListPaginate, ListParams, ListResults, TimestampMatcher,
5};
6use crate::types::state::Timestamp;
7use crate::types::state::Timestamped;
8use regex::Regex;
9
10pub fn list_values<'a, T: Clone + Timestamped>(
11 matches: &'a [(&'a Key, &'a T)],
12 filters: &'a ListParams,
13) -> ListResults<T> {
14 let matches_length = matches.len();
15
16 let ordered = order_values(matches, filters);
17
18 let start = start_at(&ordered, filters);
19
20 let paginated = paginate_values(ordered, filters, &start);
21
22 let length = paginated.len();
23
24 ListResults {
25 items: paginated,
26 items_length: length,
27 matches_length,
28 items_page: current_page(start, filters),
29 matches_pages: total_pages(matches_length, filters),
30 }
31}
32
33fn current_page(start_at: Option<usize>, filters: &ListParams) -> Option<usize> {
34 match start_at {
35 None => None,
36 Some(start_at) => match filters.clone().paginate {
37 None => None,
38 Some(paginate) => paginate.limit.map(|limit| start_at / limit),
39 },
40 }
41}
42
43fn total_pages(matches_length: usize, filters: &ListParams) -> Option<usize> {
44 match filters.clone().paginate {
45 None => None,
46 Some(paginate) => paginate.limit.map(|limit| matches_length / limit),
47 }
48}
49
50fn start_at<T: Clone + Timestamped>(matches: &[(&Key, &T)], filters: &ListParams) -> Option<usize> {
51 match filters.clone().paginate {
52 None => None,
53 Some(paginate) => match paginate.start_after {
54 None => Some(0),
55 Some(start_after) => {
56 let index = matches
57 .iter()
58 .position(|(key, _)| (*key).clone().eq(&start_after));
59 index.map(|index| index + 1)
60 }
61 },
62 }
63}
64
65fn order_values<'a, T: Clone + Timestamped>(
66 matches: &'a [(&'a Key, &'a T)],
67 ListParams {
68 matcher: _,
69 order,
70 paginate: _,
71 owner: _,
72 }: &'a ListParams,
73) -> Vec<(&'a Key, &'a T)> {
74 match order {
75 None => matches.to_vec(),
76 Some(ListOrder { desc, field }) => match field {
77 ListOrderField::Keys => order_values_with_keys(matches, desc),
78 ListOrderField::UpdatedAt => order_values_with_updated_at(matches, desc),
79 ListOrderField::CreatedAt => order_values_with_created_at(matches, desc),
80 },
81 }
82}
83
84fn order_values_with_updated_at<'a, T: Clone + Timestamped>(
85 matches: &'a [(&'a Key, &'a T)],
86 desc: &bool,
87) -> Vec<(&'a Key, &'a T)> {
88 let mut sorted_matches = matches.to_vec();
89
90 if *desc {
91 sorted_matches.sort_by(|(_, value_a), (_, value_b)| value_b.cmp_updated_at(value_a));
92 return sorted_matches;
93 }
94
95 sorted_matches.sort_by(|(_, value_a), (_, value_b)| value_a.cmp_updated_at(value_b));
96 sorted_matches
97}
98
99fn order_values_with_created_at<'a, T: Clone + Timestamped>(
100 matches: &'a [(&'a Key, &'a T)],
101 desc: &bool,
102) -> Vec<(&'a Key, &'a T)> {
103 let mut sorted_matches = matches.to_vec();
104
105 if *desc {
106 sorted_matches.sort_by(|(_, value_a), (_, value_b)| value_b.cmp_created_at(value_a));
107 return sorted_matches;
108 }
109
110 sorted_matches.sort_by(|(_, value_a), (_, value_b)| value_a.cmp_created_at(value_b));
111 sorted_matches
112}
113
114fn order_values_with_keys<'a, T: Clone + Timestamped>(
115 matches: &'a [(&'a Key, &'a T)],
116 desc: &bool,
117) -> Vec<(&'a Key, &'a T)> {
118 let mut sorted_matches = matches.to_vec();
119
120 if *desc {
121 sorted_matches.sort_by(|(key_a, _), (key_b, _)| key_b.cmp(key_a));
122 return sorted_matches;
123 }
124
125 sorted_matches.sort_by(|(key_a, _), (key_b, _)| key_a.cmp(key_b));
126 sorted_matches
127}
128
129fn paginate_values<T: Clone + Timestamped>(
130 matches: Vec<(&Key, &T)>,
131 ListParams {
132 matcher: _,
133 order: _,
134 paginate,
135 owner: _,
136 }: &ListParams,
137 start_at: &Option<usize>,
138) -> Vec<(Key, T)> {
139 match paginate {
140 None => matches
141 .iter()
142 .map(|(key, value)| ((*key).clone(), (*value).clone()))
143 .collect(),
144 Some(ListPaginate {
145 start_after: _,
146 limit,
147 }) => {
148 let max: usize = matches.len();
149
150 if max == 0 {
151 return Vec::new();
152 }
153
154 let length = match limit {
155 None => max,
156 Some(limit) => {
157 if *limit > max {
158 max
159 } else {
160 *limit
161 }
162 }
163 };
164
165 let start = match *start_at {
166 None => {
167 return Vec::new();
168 }
169 Some(start_at) => start_at,
170 };
171
172 if start > (max - 1) {
173 return Vec::new();
174 }
175
176 if start.saturating_add(length) > max - 1 {
177 return matches[start..=(max - 1)]
178 .iter()
179 .map(|(key, value)| ((*key).clone(), (*value).clone()))
180 .collect();
181 }
182
183 matches[start..=(start + length - 1)]
184 .iter()
185 .map(|(key, value)| ((*key).clone(), (*value).clone()))
186 .collect()
187 }
188 }
189}
190
191pub fn matcher_regex(
192 matcher: &Option<ListMatcher>,
193) -> Result<(Option<Regex>, Option<Regex>), String> {
194 let regex_key: Option<Regex> = match matcher {
195 None => None,
196 Some(matcher) => matcher
197 .key
198 .as_ref()
199 .map(|filter| build_regex(filter))
200 .transpose()?,
201 };
202
203 let regex_description: Option<Regex> = match matcher {
204 None => None,
205 Some(matcher) => matcher
206 .description
207 .as_ref()
208 .map(|filter| build_regex(filter))
209 .transpose()?,
210 };
211
212 Ok((regex_key, regex_description))
213}
214
215pub fn filter_timestamps<T: Timestamped>(matcher: &Option<ListMatcher>, item: &T) -> bool {
216 if let Some(matcher) = matcher {
217 if let Some(ref created_at_filter) = matcher.created_at {
218 if !match_timestamp(item.created_at(), created_at_filter) {
219 return false;
220 }
221 }
222
223 if let Some(ref updated_at_filter) = matcher.updated_at {
224 if !match_timestamp(item.updated_at(), updated_at_filter) {
225 return false;
226 }
227 }
228 }
229
230 true
231}
232
233fn match_timestamp(timestamp: Timestamp, filter: &TimestampMatcher) -> bool {
234 match filter {
235 TimestampMatcher::Equal(ts) => timestamp == *ts,
236 TimestampMatcher::GreaterThan(ts) => timestamp > *ts,
237 TimestampMatcher::LessThan(ts) => timestamp < *ts,
238 TimestampMatcher::Between(start, end) => timestamp >= *start && timestamp <= *end,
239 }
240}