sbom_tools/tui/
app_impl_search.rs1use super::app::{App, TabKind};
4use super::app_states::{
5 ChangeType, ComponentFilter, DiffSearchResult, VulnChangeType, VulnFilter, VulnSort,
6};
7
8impl App {
9 pub fn start_search(&mut self) {
11 self.overlays.search.active = true;
12 self.overlays.search.clear();
13 self.overlays.show_help = false;
14 self.overlays.show_export = false;
15 self.overlays.show_legend = false;
16 }
17
18 pub const fn stop_search(&mut self) {
20 self.overlays.search.active = false;
21 }
22
23 pub fn search_push(&mut self, c: char) {
25 self.overlays.search.push_char(c);
26 }
27
28 pub fn search_pop(&mut self) {
30 self.overlays.search.pop_char();
31 }
32
33 pub fn execute_search(&mut self) {
35 if self.overlays.search.query.len() < 2 {
36 self.overlays.search.results.clear();
37 return;
38 }
39
40 let query_lower = self.overlays.search.query.to_lowercase();
41 let mut results = Vec::new();
42
43 if let Some(ref diff) = self.data.diff_result {
45 for comp in &diff.components.added {
47 if comp.name.to_lowercase().contains(&query_lower) {
48 results.push(DiffSearchResult::Component {
49 name: comp.name.clone(),
50 version: comp.new_version.clone(),
51 change_type: ChangeType::Added,
52 });
53 }
54 }
55
56 for comp in &diff.components.removed {
58 if comp.name.to_lowercase().contains(&query_lower) {
59 results.push(DiffSearchResult::Component {
60 name: comp.name.clone(),
61 version: comp.old_version.clone(),
62 change_type: ChangeType::Removed,
63 });
64 }
65 }
66
67 for change in &diff.components.modified {
69 if change.name.to_lowercase().contains(&query_lower) {
70 results.push(DiffSearchResult::Component {
71 name: change.name.clone(),
72 version: change.new_version.clone(),
73 change_type: ChangeType::Modified,
74 });
75 }
76 }
77
78 for vuln in &diff.vulnerabilities.introduced {
80 if vuln.id.to_lowercase().contains(&query_lower) {
81 results.push(DiffSearchResult::Vulnerability {
82 id: vuln.id.clone(),
83 component_name: vuln.component_name.clone(),
84 severity: Some(vuln.severity.clone()),
85 change_type: VulnChangeType::Introduced,
86 });
87 }
88 }
89
90 for vuln in &diff.vulnerabilities.resolved {
92 if vuln.id.to_lowercase().contains(&query_lower) {
93 results.push(DiffSearchResult::Vulnerability {
94 id: vuln.id.clone(),
95 component_name: vuln.component_name.clone(),
96 severity: Some(vuln.severity.clone()),
97 change_type: VulnChangeType::Resolved,
98 });
99 }
100 }
101
102 for lic_change in &diff.licenses.new_licenses {
104 if lic_change.license.to_lowercase().contains(&query_lower) {
105 let component_name = lic_change
106 .components
107 .first()
108 .cloned()
109 .unwrap_or_else(|| "multiple".to_string());
110 results.push(DiffSearchResult::License {
111 license: lic_change.license.clone(),
112 component_name,
113 change_type: ChangeType::Added,
114 });
115 }
116 }
117
118 for lic_change in &diff.licenses.removed_licenses {
120 if lic_change.license.to_lowercase().contains(&query_lower) {
121 let component_name = lic_change
122 .components
123 .first()
124 .cloned()
125 .unwrap_or_else(|| "multiple".to_string());
126 results.push(DiffSearchResult::License {
127 license: lic_change.license.clone(),
128 component_name,
129 change_type: ChangeType::Removed,
130 });
131 }
132 }
133 }
134
135 if self.data.diff_result.is_none()
137 && let Some(ref sbom) = self.data.sbom
138 {
139 for comp in sbom.components.values() {
141 if comp.name.to_lowercase().contains(&query_lower) {
142 results.push(DiffSearchResult::Component {
143 name: comp.name.clone(),
144 version: comp.version.clone(),
145 change_type: ChangeType::Added, });
147 }
148 }
149
150 for comp in sbom.components.values() {
152 for vuln in &comp.vulnerabilities {
153 if vuln.id.to_lowercase().contains(&query_lower) {
154 results.push(DiffSearchResult::Vulnerability {
155 id: vuln.id.clone(),
156 component_name: comp.name.clone(),
157 severity: vuln.severity.as_ref().map(|s| format!("{s:?}")),
158 change_type: VulnChangeType::Introduced, });
160 }
161 }
162 }
163
164 for comp in sbom.components.values() {
166 for lic in &comp.licenses.declared {
167 if lic.expression.to_lowercase().contains(&query_lower) {
168 results.push(DiffSearchResult::License {
169 license: lic.expression.clone(),
170 component_name: comp.name.clone(),
171 change_type: ChangeType::Added, });
173 }
174 }
175 }
176 }
177
178 results.truncate(50);
180 self.overlays.search.results = results;
181 self.overlays.search.selected = 0;
182 }
183
184 pub fn jump_to_search_result(&mut self) {
186 if let Some(result) = self
187 .overlays
188 .search
189 .results
190 .get(self.overlays.search.selected)
191 .cloned()
192 {
193 match result {
194 DiffSearchResult::Component {
195 name,
196 version,
197 change_type,
198 ..
199 } => {
200 if let Some(index) =
202 self.find_component_index_all(&name, Some(change_type), version.as_deref())
203 {
204 self.tabs.components.filter = ComponentFilter::All;
205 self.tabs.components.selected = index;
206 self.select_tab(TabKind::Components);
207 self.stop_search();
208 return;
209 }
210
211 if let Some(index) = self.find_component_index_all(&name, None, None) {
213 self.tabs.components.filter = ComponentFilter::All;
214 self.tabs.components.selected = index;
215 self.select_tab(TabKind::Components);
216 self.stop_search();
217 return;
218 }
219
220 self.tabs.components.filter = ComponentFilter::All;
221 self.select_tab(TabKind::Components);
222 }
223 DiffSearchResult::Vulnerability {
224 id, change_type, ..
225 } => {
226 self.tabs.vulnerabilities.sort_by = VulnSort::Id;
228 self.tabs.vulnerabilities.filter = match change_type {
229 VulnChangeType::Introduced => VulnFilter::Introduced,
230 VulnChangeType::Resolved => VulnFilter::Resolved,
231 };
232
233 if let Some(index) = self.find_vulnerability_index(&id) {
234 self.tabs.vulnerabilities.selected = index;
235 }
236
237 self.select_tab(TabKind::Vulnerabilities);
238 }
239 DiffSearchResult::License { license, .. } => {
240 if let Some(ref diff) = self.data.diff_result {
242 let mut index = 0;
243
244 for lic in &diff.licenses.new_licenses {
246 if lic.license == license {
247 self.tabs.licenses.selected = index;
248 self.select_tab(TabKind::Licenses);
249 self.stop_search();
250 return;
251 }
252 index += 1;
253 }
254
255 for lic in &diff.licenses.removed_licenses {
257 if lic.license == license {
258 self.tabs.licenses.selected = index;
259 self.select_tab(TabKind::Licenses);
260 self.stop_search();
261 return;
262 }
263 index += 1;
264 }
265 }
266 self.select_tab(TabKind::Licenses);
267 }
268 }
269 self.stop_search();
270 }
271 }
272}