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