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 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 match_field: "name".to_string(),
53 });
54 }
55 }
56
57 for comp in &diff.components.removed {
59 if comp.name.to_lowercase().contains(&query_lower) {
60 results.push(DiffSearchResult::Component {
61 name: comp.name.clone(),
62 version: comp.old_version.clone(),
63 change_type: ChangeType::Removed,
64 match_field: "name".to_string(),
65 });
66 }
67 }
68
69 for change in &diff.components.modified {
71 if change.name.to_lowercase().contains(&query_lower) {
72 results.push(DiffSearchResult::Component {
73 name: change.name.clone(),
74 version: change.new_version.clone(),
75 change_type: ChangeType::Modified,
76 match_field: "name".to_string(),
77 });
78 }
79 }
80
81 for vuln in &diff.vulnerabilities.introduced {
83 if vuln.id.to_lowercase().contains(&query_lower) {
84 results.push(DiffSearchResult::Vulnerability {
85 id: vuln.id.clone(),
86 component_name: vuln.component_name.clone(),
87 severity: Some(vuln.severity.clone()),
88 change_type: VulnChangeType::Introduced,
89 });
90 }
91 }
92
93 for vuln in &diff.vulnerabilities.resolved {
95 if vuln.id.to_lowercase().contains(&query_lower) {
96 results.push(DiffSearchResult::Vulnerability {
97 id: vuln.id.clone(),
98 component_name: vuln.component_name.clone(),
99 severity: Some(vuln.severity.clone()),
100 change_type: VulnChangeType::Resolved,
101 });
102 }
103 }
104
105 for lic_change in &diff.licenses.new_licenses {
107 if lic_change.license.to_lowercase().contains(&query_lower) {
108 let component_name = lic_change
109 .components
110 .first()
111 .cloned()
112 .unwrap_or_else(|| "multiple".to_string());
113 results.push(DiffSearchResult::License {
114 license: lic_change.license.clone(),
115 component_name,
116 change_type: ChangeType::Added,
117 });
118 }
119 }
120
121 for lic_change in &diff.licenses.removed_licenses {
123 if lic_change.license.to_lowercase().contains(&query_lower) {
124 let component_name = lic_change
125 .components
126 .first()
127 .cloned()
128 .unwrap_or_else(|| "multiple".to_string());
129 results.push(DiffSearchResult::License {
130 license: lic_change.license.clone(),
131 component_name,
132 change_type: ChangeType::Removed,
133 });
134 }
135 }
136 }
137
138 results.truncate(50);
140 self.overlays.search.results = results;
141 self.overlays.search.selected = 0;
142 }
143
144 pub fn jump_to_search_result(&mut self) {
146 if let Some(result) = self
147 .overlays.search
148 .results
149 .get(self.overlays.search.selected)
150 .cloned()
151 {
152 match result {
153 DiffSearchResult::Component {
154 name,
155 version,
156 change_type,
157 ..
158 } => {
159 if let Some(index) =
161 self.find_component_index_all(&name, Some(change_type), version.as_deref())
162 {
163 self.tabs.components.filter = ComponentFilter::All;
164 self.tabs.components.selected = index;
165 self.select_tab(TabKind::Components);
166 self.stop_search();
167 return;
168 }
169
170 if let Some(index) = self.find_component_index_all(&name, None, None) {
172 self.tabs.components.filter = ComponentFilter::All;
173 self.tabs.components.selected = index;
174 self.select_tab(TabKind::Components);
175 self.stop_search();
176 return;
177 }
178
179 self.tabs.components.filter = ComponentFilter::All;
180 self.select_tab(TabKind::Components);
181 }
182 DiffSearchResult::Vulnerability {
183 id, change_type, ..
184 } => {
185 self.tabs.vulnerabilities.sort_by = VulnSort::Id;
187 self.tabs.vulnerabilities.filter = match change_type {
188 VulnChangeType::Introduced => VulnFilter::Introduced,
189 VulnChangeType::Resolved => VulnFilter::Resolved,
190 VulnChangeType::Persistent => VulnFilter::All,
191 };
192
193 if let Some(index) = self.find_vulnerability_index(&id) {
194 self.tabs.vulnerabilities.selected = index;
195 }
196
197 self.select_tab(TabKind::Vulnerabilities);
198 }
199 DiffSearchResult::License { license, .. } => {
200 if let Some(ref diff) = self.data.diff_result {
202 let mut index = 0;
203
204 for lic in &diff.licenses.new_licenses {
206 if lic.license == license {
207 self.tabs.licenses.selected = index;
208 self.select_tab(TabKind::Licenses);
209 self.stop_search();
210 return;
211 }
212 index += 1;
213 }
214
215 for lic in &diff.licenses.removed_licenses {
217 if lic.license == license {
218 self.tabs.licenses.selected = index;
219 self.select_tab(TabKind::Licenses);
220 self.stop_search();
221 return;
222 }
223 index += 1;
224 }
225 }
226 self.select_tab(TabKind::Licenses);
227 }
228 }
229 self.stop_search();
230 }
231 }
232}