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
78 fn create_engine(
79 engine_id: &str,
80 order_hint: Option<u32>,
81 name: Option<&str>,
82 ) -> SearchEngineDefinition {
83 SearchEngineDefinition {
84 identifier: engine_id.to_string(),
85 name: name.unwrap_or(engine_id).to_string(),
86 order_hint,
87 ..Default::default()
88 }
89 }
90
91 #[test]
92 fn test_find_engine_with_match_mut_starts_with() {
93 let mut engines = vec![
94 create_engine("wiki-ca", None, None),
95 create_engine("wiki-uk", None, None),
96 create_engine("test-engine", None, None),
97 ];
98 let found_engine = find_engine_with_match_mut(&mut engines, &"wiki*".to_string());
99
100 assert_eq!(
101 found_engine.unwrap().identifier,
102 "wiki-ca",
103 "Should match the first engine that starts with 'wiki'."
104 );
105 }
106
107 #[test]
108 fn test_set_engine_order_full_list() {
109 let mut engines = vec![
110 create_engine("last-engine", None, None),
111 create_engine("secondary-engine", None, None),
112 create_engine("primary-engine", None, None),
113 ];
114 let ordered_engines_list = vec![
115 "primary-engine".to_string(),
116 "secondary-engine".to_string(),
117 "last-engine".to_string(),
118 ];
119 set_engine_order(&mut engines, &ordered_engines_list);
120
121 let expected_order_hints = vec![
122 ("last-engine", Some(1)),
123 ("secondary-engine", Some(2)),
124 ("primary-engine", Some(3)),
125 ];
126 let actual_order_hints: Vec<(&str, Option<u32>)> = engines
127 .iter()
128 .map(|e| (e.identifier.as_str(), e.order_hint))
129 .collect();
130
131 assert_eq!(
132 actual_order_hints, expected_order_hints,
133 "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."
134 )
135 }
136
137 #[test]
138 fn test_set_engine_order_partial_list() {
139 let mut engines = vec![
140 create_engine("secondary-engine", None, None),
141 create_engine("primary-engine", None, None),
142 create_engine("no-order-hint-engine", None, None),
143 ];
144 let ordered_engines_list =
145 vec!["primary-engine".to_string(), "secondary-engine".to_string()];
146 set_engine_order(&mut engines, &ordered_engines_list);
147
148 let expected_order_hints = vec![
149 ("secondary-engine", Some(1)),
150 ("primary-engine", Some(2)),
151 ("no-order-hint-engine", None),
152 ];
153 let actual_order_hints: Vec<(&str, Option<u32>)> = engines
154 .iter()
155 .map(|e| (e.identifier.as_str(), e.order_hint))
156 .collect();
157 assert_eq!(
158 actual_order_hints, expected_order_hints,
159 "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."
160 )
161 }
162
163 #[test]
164 fn test_sort_engines_by_order_hint() {
165 let default_engine_id = None;
166 let default_private_engine_id = None;
167 let mut engines = vec![
168 create_engine("c-engine", Some(3), None),
169 create_engine("b-engine", Some(2), None),
170 create_engine("a-engine", Some(1), None),
171 ];
172 engines.sort_by(|a, b| {
173 sort(
174 default_engine_id.as_ref(),
175 default_private_engine_id.as_ref(),
176 a,
177 b,
178 )
179 });
180
181 let actual_order: Vec<&str> = engines.iter().map(|e| e.identifier.as_str()).collect();
182 let expected_order = vec!["c-engine", "b-engine", "a-engine"];
183 assert_eq!(
184 actual_order, expected_order,
185 "Should sort engines by descending order hint, with the highest order hint appearing first."
186 )
187 }
188
189 #[test]
190 fn test_sort_engines_alphabetically_without_order_hint() {
191 let default_engine_id = None;
192 let default_private_engine_id = None;
193 let mut engines = vec![
194 create_engine("c-engine", None, None),
195 create_engine("b-engine", None, None),
196 create_engine("a-engine", None, None),
197 ];
198 engines.sort_by(|a, b| {
199 sort(
200 default_engine_id.as_ref(),
201 default_private_engine_id.as_ref(),
202 a,
203 b,
204 )
205 });
206
207 let actual_order: Vec<&str> = engines.iter().map(|e| e.identifier.as_str()).collect();
208 let expected_order = vec!["a-engine", "b-engine", "c-engine"];
209 assert_eq!(
210 actual_order, expected_order,
211 "Should sort engines alphabetically when there are no order hints."
212 )
213 }
214
215 #[test]
216 fn test_sort_engines_by_order_hint_and_alphabetically() {
217 let default_engine_id = None;
218 let default_private_engine_id = None;
219 let mut engines = vec![
220 create_engine("d-engine", None, Some("Charlie")),
223 create_engine("e-engine", None, Some("Beta")),
224 create_engine("f-engine", None, Some("Alpha")),
225 create_engine("c-engine", Some(4), None),
226 create_engine("b-engine", Some(5), None),
227 create_engine("a-engine", Some(6), None),
228 ];
229 engines.sort_by(|a, b| {
230 sort(
231 default_engine_id.as_ref(),
232 default_private_engine_id.as_ref(),
233 a,
234 b,
235 )
236 });
237
238 let actual_order: Vec<&str> = engines.iter().map(|e| e.identifier.as_str()).collect();
239 let expected_order = vec![
240 "a-engine", "b-engine", "c-engine", "f-engine", "e-engine", "d-engine",
241 ];
242 assert_eq!(
243 actual_order, expected_order,
244 "Should sort engines by order hint before sorting alphabetically."
245 )
246 }
247
248 #[test]
249 fn test_sort_engines_with_defaults() {
250 let default_engine_id = Some("a-engine".to_string());
251 let default_private_engine_id = Some("b-engine".to_string());
252 let mut engines = vec![
253 create_engine("c-engine", Some(3), None),
254 create_engine("a-engine", Some(1), None), create_engine("b-engine", Some(2), None), ];
257 engines.sort_by(|a, b| {
258 sort(
259 default_engine_id.as_ref(),
260 default_private_engine_id.as_ref(),
261 a,
262 b,
263 )
264 });
265
266 let actual_order: Vec<&str> = engines.iter().map(|e| e.identifier.as_str()).collect();
267 let expected_order = vec!["a-engine", "b-engine", "c-engine"];
268 assert_eq!(
269 actual_order, expected_order,
270 "Should have sorted the default and private default to have the highest priority."
271 )
272 }
273
274 #[test]
275 fn test_sort_engines_non_ascii_without_order_hint() {
276 }
279}