1use crate::state::{AppState, PackageItem, Source};
4
5#[inline]
6fn return_if_true(cond: bool, it: PackageItem, out: &mut Vec<PackageItem>) {
19 if cond {
20 out.push(it);
21 }
22}
23
24pub fn apply_filters_and_sort_preserve_selection(app: &mut AppState) {
36 let prev_name = app.results.get(app.selected).map(|p| p.name.clone());
38
39 let mut filtered: Vec<PackageItem> = Vec::with_capacity(app.all_results.len());
41 for it in app.all_results.iter().cloned() {
42 let include = match &it.source {
43 Source::Aur => app.results_filter_show_aur,
44 Source::Official { repo, .. } => {
45 let owner = app
48 .details_cache
49 .get(&it.name)
50 .map(|d| d.owner.clone())
51 .unwrap_or_default();
52 if crate::index::is_manjaro_name_or_owner(&it.name, &owner) {
53 return_if_true(app.results_filter_show_manjaro, it, &mut filtered);
54 continue;
55 }
56 crate::logic::distro::repo_toggle_for(repo, app)
57 }
58 };
59 if include {
60 filtered.push(it);
61 }
62 }
63 app.results = filtered;
64 crate::logic::invalidate_sort_caches(app);
66 crate::logic::sort_results_preserve_selection(app);
68 if let Some(name) = prev_name {
70 if let Some(pos) = app.results.iter().position(|p| p.name == name) {
71 app.selected = pos;
72 app.list_state.select(Some(pos));
73 } else if !app.results.is_empty() {
74 app.selected = app.selected.min(app.results.len() - 1);
75 app.list_state.select(Some(app.selected));
76 } else {
77 app.selected = 0;
78 app.list_state.select(None);
79 }
80 } else if app.results.is_empty() {
81 app.selected = 0;
82 app.list_state.select(None);
83 } else {
84 app.selected = app.selected.min(app.results.len() - 1);
85 app.list_state.select(Some(app.selected));
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 fn item_official(name: &str, repo: &str) -> PackageItem {
94 PackageItem {
95 name: name.to_string(),
96 version: "1.0".to_string(),
97 description: format!("{name} desc"),
98 source: Source::Official {
99 repo: repo.to_string(),
100 arch: "x86_64".to_string(),
101 },
102 popularity: None,
103 out_of_date: None,
104 orphaned: false,
105 }
106 }
107
108 #[test]
109 fn apply_filters_and_preserve_selection() {
121 let mut app = AppState {
122 all_results: vec![
123 PackageItem {
124 name: "aur1".into(),
125 version: "1".into(),
126 description: String::new(),
127 source: Source::Aur,
128 popularity: Some(1.0),
129 out_of_date: None,
130 orphaned: false,
131 },
132 item_official("core1", "core"),
133 item_official("extra1", "extra"),
134 item_official("other1", "community"),
135 ],
136 results_filter_show_aur: false,
137 results_filter_show_core: true,
138 results_filter_show_extra: false,
139 results_filter_show_multilib: false,
140 ..Default::default()
141 };
142 apply_filters_and_sort_preserve_selection(&mut app);
143 assert!(app.results.iter().all(
144 |p| matches!(&p.source, Source::Official{repo, ..} if repo.eq_ignore_ascii_case("core"))
145 ));
146 }
147
148 #[test]
149 fn apply_filters_cachyos_and_eos_interaction() {
160 let mut app = AppState {
161 all_results: vec![
162 PackageItem {
163 name: "cx".into(),
164 version: "1".into(),
165 description: String::new(),
166 source: Source::Official {
167 repo: "cachyos-core".into(),
168 arch: "x86_64".into(),
169 },
170 popularity: None,
171 out_of_date: None,
172 orphaned: false,
173 },
174 PackageItem {
175 name: "ey".into(),
176 version: "1".into(),
177 description: String::new(),
178 source: Source::Official {
179 repo: "endeavouros".into(),
180 arch: "x86_64".into(),
181 },
182 popularity: None,
183 out_of_date: None,
184 orphaned: false,
185 },
186 item_official("core1", "core"),
187 ],
188 results_filter_show_core: true,
189 results_filter_show_extra: true,
190 results_filter_show_multilib: true,
191 results_filter_show_eos: false,
192 results_filter_show_cachyos: true,
193 ..Default::default()
194 };
195 apply_filters_and_sort_preserve_selection(&mut app);
196 assert!(app.results.iter().any(|p| match &p.source {
197 Source::Official { repo, .. } => repo.to_lowercase().starts_with("cachyos"),
198 Source::Aur => false,
199 }));
200 assert!(app.results.iter().all(|p| match &p.source {
201 Source::Official { repo, .. } => !repo.eq_ignore_ascii_case("endeavouros"),
202 Source::Aur => true,
203 }));
204 }
205
206 #[test]
207 fn logic_filter_unknown_official_inclusion_policy() {
219 let mut app = AppState {
220 all_results: vec![
221 PackageItem {
222 name: "x1".into(),
223 version: "1".into(),
224 description: String::new(),
225 source: Source::Official {
226 repo: "weirdrepo".into(),
227 arch: "x86_64".into(),
228 },
229 popularity: None,
230 out_of_date: None,
231 orphaned: false,
232 },
233 item_official("core1", "core"),
234 ],
235 results_filter_show_aur: true,
236 results_filter_show_core: true,
237 results_filter_show_extra: true,
238 results_filter_show_multilib: false,
239 results_filter_show_eos: true,
240 results_filter_show_cachyos: true,
241 ..Default::default()
242 };
243 apply_filters_and_sort_preserve_selection(&mut app);
244 assert!(app.results.iter().all(|p| match &p.source {
245 Source::Official { repo, .. } => repo.eq_ignore_ascii_case("core"),
246 Source::Aur => false,
247 }));
248
249 app.results_filter_show_multilib = true;
250 apply_filters_and_sort_preserve_selection(&mut app);
251 assert!(app.results.iter().any(|p| match &p.source {
252 Source::Official { repo, .. } => repo.eq_ignore_ascii_case("weirdrepo"),
253 Source::Aur => false,
254 }));
255 }
256}