1use crate::SearchEngineDefinition;
9
10pub(crate) fn set_engine_order(engines: &mut [SearchEngineDefinition], ordered_engines: &[String]) {
11 let mut order_number = ordered_engines.len();
12
13 for engine_id in ordered_engines {
14 if let Some(found_engine) = find_engine_with_match_mut(engines, engine_id) {
15 found_engine.order_hint = Some(order_number as u32);
16 order_number -= 1;
17 }
18 }
19}
20
21pub(crate) fn sort(
22 default_engine_id: Option<&String>,
23 default_private_engine_id: Option<&String>,
24 a: &SearchEngineDefinition,
25 b: &SearchEngineDefinition,
26) -> std::cmp::Ordering {
27 let b_index = get_priority(b, default_engine_id, default_private_engine_id);
28 let a_index = get_priority(a, default_engine_id, default_private_engine_id);
29 let order = b_index.cmp(&a_index);
30
31 if order == std::cmp::Ordering::Equal {
35 return a.name.cmp(&b.name);
36 }
37
38 order
39}
40
41fn find_engine_with_match_mut<'a>(
42 engines: &'a mut [SearchEngineDefinition],
43 engine_id_match: &String,
44) -> Option<&'a mut SearchEngineDefinition> {
45 if engine_id_match.is_empty() {
46 return None;
47 }
48 if let Some(match_no_star) = engine_id_match.strip_suffix('*') {
49 return engines
50 .iter_mut()
51 .find(|e| e.identifier.starts_with(match_no_star));
52 }
53
54 engines
55 .iter_mut()
56 .find(|e| e.identifier == *engine_id_match)
57}
58
59fn get_priority(
60 engine: &SearchEngineDefinition,
61 default_engine_id: Option<&String>,
62 default_private_engine_id: Option<&String>,
63) -> u32 {
64 if Some(&engine.identifier) == default_engine_id {
65 return u32::MAX;
66 }
67 if Some(&engine.identifier) == default_private_engine_id {
68 return u32::MAX - 1;
69 }
70 engine.order_hint.unwrap_or(0)
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use crate::types::*;
77 use pretty_assertions::assert_eq;
78
79 fn create_engine(
80 engine_id: &str,
81 order_hint: Option<u32>,
82 name: Option<&str>,
83 ) -> SearchEngineDefinition {
84 SearchEngineDefinition {
85 identifier: engine_id.to_string(),
86 name: name.unwrap_or(engine_id).to_string(),
87 order_hint,
88 ..Default::default()
89 }
90 }
91
92 #[test]
93 fn test_find_engine_with_match_mut_starts_with() {
94 let mut engines = vec![
95 create_engine("wiki-ca", None, None),
96 create_engine("wiki-uk", None, None),
97 create_engine("test-engine", None, None),
98 ];
99 let found_engine = find_engine_with_match_mut(&mut engines, &"wiki*".to_string());
100
101 assert_eq!(
102 found_engine.unwrap().identifier,
103 "wiki-ca",
104 "Should match the first engine that starts with 'wiki'."
105 );
106 }
107
108 #[test]
109 fn test_set_engine_order_full_list() {
110 let mut engines = vec![
111 create_engine("last-engine", None, None),
112 create_engine("secondary-engine", None, None),
113 create_engine("primary-engine", None, None),
114 ];
115 let ordered_engines_list = vec![
116 "primary-engine".to_string(),
117 "secondary-engine".to_string(),
118 "last-engine".to_string(),
119 ];
120 set_engine_order(&mut engines, &ordered_engines_list);
121
122 let expected_order_hints = vec![
123 ("last-engine", Some(1)),
124 ("secondary-engine", Some(2)),
125 ("primary-engine", Some(3)),
126 ];
127 let actual_order_hints: Vec<(&str, Option<u32>)> = engines
128 .iter()
129 .map(|e| (e.identifier.as_str(), e.order_hint))
130 .collect();
131
132 assert_eq!(
133 actual_order_hints, expected_order_hints,
134 "Should assign correct order hints when all engines are in the ordered engines list, the first engine with the highest and decreasing for each next engine."
135 )
136 }
137
138 #[test]
139 fn test_set_engine_order_partial_list() {
140 let mut engines = vec![
141 create_engine("secondary-engine", None, None),
142 create_engine("primary-engine", None, None),
143 create_engine("no-order-hint-engine", None, None),
144 ];
145 let ordered_engines_list =
146 vec!["primary-engine".to_string(), "secondary-engine".to_string()];
147 set_engine_order(&mut engines, &ordered_engines_list);
148
149 let expected_order_hints = vec![
150 ("secondary-engine", Some(1)),
151 ("primary-engine", Some(2)),
152 ("no-order-hint-engine", None),
153 ];
154 let actual_order_hints: Vec<(&str, Option<u32>)> = engines
155 .iter()
156 .map(|e| (e.identifier.as_str(), e.order_hint))
157 .collect();
158 assert_eq!(
159 actual_order_hints, expected_order_hints,
160 "Should assign correct order hints when some of the engines are in the ordered engines list, the first engine with the highest and decreasing for each next engine."
161 )
162 }
163
164 #[test]
165 fn test_sort_engines_by_order_hint() {
166 let default_engine_id = None;
167 let default_private_engine_id = None;
168 let mut engines = vec![
169 create_engine("c-engine", Some(3), None),
170 create_engine("b-engine", Some(2), None),
171 create_engine("a-engine", Some(1), None),
172 ];
173 engines.sort_by(|a, b| {
174 sort(
175 default_engine_id.as_ref(),
176 default_private_engine_id.as_ref(),
177 a,
178 b,
179 )
180 });
181
182 let actual_order: Vec<&str> = engines.iter().map(|e| e.identifier.as_str()).collect();
183 let expected_order = vec!["c-engine", "b-engine", "a-engine"];
184 assert_eq!(
185 actual_order, expected_order,
186 "Should sort engines by descending order hint, with the highest order hint appearing first."
187 )
188 }
189
190 #[test]
191 fn test_sort_engines_alphabetically_without_order_hint() {
192 let default_engine_id = None;
193 let default_private_engine_id = None;
194 let mut engines = vec![
195 create_engine("c-engine", None, None),
196 create_engine("b-engine", None, None),
197 create_engine("a-engine", None, None),
198 ];
199 engines.sort_by(|a, b| {
200 sort(
201 default_engine_id.as_ref(),
202 default_private_engine_id.as_ref(),
203 a,
204 b,
205 )
206 });
207
208 let actual_order: Vec<&str> = engines.iter().map(|e| e.identifier.as_str()).collect();
209 let expected_order = vec!["a-engine", "b-engine", "c-engine"];
210 assert_eq!(
211 actual_order, expected_order,
212 "Should sort engines alphabetically when there are no order hints."
213 )
214 }
215
216 #[test]
217 fn test_sort_engines_by_order_hint_and_alphabetically() {
218 let default_engine_id = None;
219 let default_private_engine_id = None;
220 let mut engines = vec![
221 create_engine("d-engine", None, Some("Charlie")),
224 create_engine("e-engine", None, Some("Beta")),
225 create_engine("f-engine", None, Some("Alpha")),
226 create_engine("c-engine", Some(4), None),
227 create_engine("b-engine", Some(5), None),
228 create_engine("a-engine", Some(6), None),
229 ];
230 engines.sort_by(|a, b| {
231 sort(
232 default_engine_id.as_ref(),
233 default_private_engine_id.as_ref(),
234 a,
235 b,
236 )
237 });
238
239 let actual_order: Vec<&str> = engines.iter().map(|e| e.identifier.as_str()).collect();
240 let expected_order = vec![
241 "a-engine", "b-engine", "c-engine", "f-engine", "e-engine", "d-engine",
242 ];
243 assert_eq!(
244 actual_order, expected_order,
245 "Should sort engines by order hint before sorting alphabetically."
246 )
247 }
248
249 #[test]
250 fn test_sort_engines_with_defaults() {
251 let default_engine_id = Some("a-engine".to_string());
252 let default_private_engine_id = Some("b-engine".to_string());
253 let mut engines = vec![
254 create_engine("c-engine", Some(3), None),
255 create_engine("a-engine", Some(1), None), create_engine("b-engine", Some(2), None), ];
258 engines.sort_by(|a, b| {
259 sort(
260 default_engine_id.as_ref(),
261 default_private_engine_id.as_ref(),
262 a,
263 b,
264 )
265 });
266
267 let actual_order: Vec<&str> = engines.iter().map(|e| e.identifier.as_str()).collect();
268 let expected_order = vec!["a-engine", "b-engine", "c-engine"];
269 assert_eq!(
270 actual_order, expected_order,
271 "Should have sorted the default and private default to have the highest priority."
272 )
273 }
274
275 #[test]
276 fn test_sort_engines_non_ascii_without_order_hint() {
277 }
280}