1use std::collections::HashMap;
2use std::hash::Hash;
3
4pub trait RullstCollection<T> {
6 fn key_by<K, F>(self, f: F) -> HashMap<K, T>
8 where
9 F: Fn(&T) -> K,
10 K: Hash + Eq;
11
12 fn chunk(self, size: usize) -> Vec<Vec<T>>;
14
15 fn implode<F>(&self, separator: &str, f: F) -> String
17 where
18 F: Fn(&T) -> String;
19
20 fn sum_by<N, F>(&self, f: F) -> N
22 where
23 F: Fn(&T) -> N,
24 N: std::iter::Sum;
25
26 fn max_by_key<K, F>(&self, f: F) -> Option<&T>
28 where
29 F: Fn(&T) -> K,
30 K: Ord;
31
32 fn min_by_key<K, F>(&self, f: F) -> Option<&T>
34 where
35 F: Fn(&T) -> K,
36 K: Ord;
37
38 fn collection_resource(&self) -> serde_json::Value
40 where
41 T: crate::resource::ApiResource;
42}
43
44impl<T> RullstCollection<T> for Vec<T> {
45 fn key_by<K, F>(self, f: F) -> HashMap<K, T>
46 where
47 F: Fn(&T) -> K,
48 K: Hash + Eq,
49 {
50 let mut map = HashMap::with_capacity(self.len());
51 for item in self {
52 map.insert(f(&item), item);
53 }
54 map
55 }
56
57 fn chunk(self, size: usize) -> Vec<Vec<T>> {
58 if size == 0 {
59 return vec![self];
60 }
61
62 let mut chunks = Vec::with_capacity(self.len().div_ceil(size));
63 let mut current_chunk = Vec::with_capacity(size);
64
65 for item in self {
66 current_chunk.push(item);
67 if current_chunk.len() == size {
68 chunks.push(current_chunk);
69 current_chunk = Vec::with_capacity(size);
70 }
71 }
72
73 if !current_chunk.is_empty() {
74 chunks.push(current_chunk);
75 }
76
77 chunks
78 }
79
80 fn implode<F>(&self, separator: &str, f: F) -> String
81 where
82 F: Fn(&T) -> String,
83 {
84 let mut result = String::new();
85 let mut iter = self.iter();
86 if let Some(first) = iter.next() {
87 result.push_str(&f(first));
88 for item in iter {
89 result.push_str(separator);
90 result.push_str(&f(item));
91 }
92 }
93 result
94 }
95
96 fn sum_by<N, F>(&self, f: F) -> N
97 where
98 F: Fn(&T) -> N,
99 N: std::iter::Sum,
100 {
101 self.iter().map(f).sum()
102 }
103
104 fn max_by_key<K, F>(&self, f: F) -> Option<&T>
105 where
106 F: Fn(&T) -> K,
107 K: Ord,
108 {
109 self.iter().max_by_key(|item| f(*item))
110 }
111
112 fn min_by_key<K, F>(&self, f: F) -> Option<&T>
113 where
114 F: Fn(&T) -> K,
115 K: Ord,
116 {
117 self.iter().min_by_key(|item| f(*item))
118 }
119
120 fn collection_resource(&self) -> serde_json::Value
121 where
122 T: crate::resource::ApiResource,
123 {
124 crate::resource::ResourceCollection::new(self).resolve()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_key_by() {
134 let v = vec![(1u32, "a"), (2, "b"), (3, "c")];
135 let map = v.key_by(|(k, _)| *k);
136 assert_eq!(map[&1].1, "a");
137 assert_eq!(map[&3].1, "c");
138 }
139
140 #[test]
141 fn test_chunk_even() {
142 let v = vec![1, 2, 3, 4];
143 let chunks = v.chunk(2);
144 assert_eq!(chunks.len(), 2);
145 assert_eq!(chunks[0], vec![1, 2]);
146 assert_eq!(chunks[1], vec![3, 4]);
147 }
148
149 #[test]
150 fn test_chunk_with_remainder() {
151 let v = vec![1, 2, 3, 4, 5];
152 let chunks = v.chunk(2);
153 assert_eq!(chunks.len(), 3);
154 assert_eq!(chunks[2], vec![5]);
155 }
156
157 #[test]
158 fn test_chunk_zero_returns_all() {
159 let v = vec![1, 2, 3];
160 let chunks = v.chunk(0);
161 assert_eq!(chunks.len(), 1);
162 assert_eq!(chunks[0], vec![1, 2, 3]);
163 }
164
165 #[test]
166 fn test_implode() {
167 let v = vec![1, 2, 3];
168 let result = v.implode(", ", |n| n.to_string());
169 assert_eq!(result, "1, 2, 3");
170 }
171
172 #[test]
173 fn test_implode_single_element() {
174 let v = vec![42];
175 let result = v.implode(", ", |n| n.to_string());
176 assert_eq!(result, "42");
177 }
178
179 #[test]
180 fn test_sum_by() {
181 let v = vec![1, 2, 3, 4];
182 let sum: i32 = v.sum_by(|n| *n);
183 assert_eq!(sum, 10);
184 }
185
186 #[test]
187 fn test_max_by_key() {
188 let v = vec![3, 1, 4, 1, 5, 9];
189 let max = v.max_by_key(|n| *n);
190 assert_eq!(max, Some(&9));
191 }
192
193 #[test]
194 fn test_min_by_key() {
195 let v = vec![3, 1, 4, 1, 5, 9];
196 let min = v.min_by_key(|n| *n);
197 assert_eq!(min, Some(&1));
198 }
199
200 #[test]
201 fn test_empty_collection() {
202 let v: Vec<i32> = vec![];
203 assert!(v.max_by_key(|n| *n).is_none());
204 assert!(v.min_by_key(|n| *n).is_none());
205 let sum: i32 = v.sum_by(|n| *n);
206 assert_eq!(sum, 0);
207 }
208}