sbom_tools/tui/
app_impl_constructors.rs1use super::app::{App, AppMode, AppOverlays, DataContext, ModeStates, TabKind};
4use super::app_states::{
5 MatrixState, MultiDiffState, NavigationContext, SourceDiffState, TimelineState,
6};
7use crate::diff::{DiffResult, MatrixResult, MultiDiffResult, TimelineResult};
8use crate::model::NormalizedSbom;
9use crate::quality::{ComplianceChecker, ComplianceLevel, QualityScorer};
10
11impl App {
12 fn base(mode: AppMode) -> Self {
15 Self {
16 mode,
17 active_tab: crate::config::TuiPreferences::load()
18 .last_tab
19 .as_deref()
20 .and_then(TabKind::from_str_opt)
21 .unwrap_or(TabKind::Summary),
22 data: DataContext {
23 diff_result: None,
24 old_sbom: None,
25 new_sbom: None,
26 sbom: None,
27 multi_diff_result: None,
28 timeline_result: None,
29 matrix_result: None,
30 old_sbom_index: None,
31 new_sbom_index: None,
32 sbom_index: None,
33 old_quality: None,
34 new_quality: None,
35 quality_report: None,
36 old_cra_compliance: None,
37 new_cra_compliance: None,
38 old_compliance_results: None,
39 new_compliance_results: None,
40 cra_sidecar: None,
41 matching_threshold: 0.85,
42 #[cfg(feature = "enrichment")]
43 enrichment_stats_old: None,
44 #[cfg(feature = "enrichment")]
45 enrichment_stats_new: None,
46 },
47 tabs: ModeStates {
48 multi_diff: MultiDiffState::new(),
49 timeline: TimelineState::new(),
50 matrix: MatrixState::new(),
51 },
52 overlays: AppOverlays::new(),
53 should_quit: false,
54 status_message: None,
55 status_sticky: false,
56 tick: 0,
57 last_export_path: None,
58 navigation_ctx: NavigationContext::new(),
59 security_cache: crate::tui::security::SecurityAnalysisCache::new(),
60 compliance_state: crate::tui::app_states::PolicyComplianceState::new(),
61 export_template: None,
62 components_view: crate::tui::view_states::ComponentsView::new(),
63 dependencies_view: crate::tui::view_states::DependenciesView::new(),
64 licenses_view: crate::tui::view_states::LicensesView::new(),
65 vulnerabilities_view: crate::tui::view_states::VulnerabilitiesView::new(),
66 quality_view: crate::tui::view_states::QualityView::new(),
67 compliance_view: crate::tui::view_states::ComplianceView::new(),
68 sidebyside_view: crate::tui::view_states::SideBySideView::new(),
69 graph_changes_view: crate::tui::view_states::GraphChangesView::new(),
70 source_view: crate::tui::view_states::SourceView::new(),
71 }
72 }
73
74 #[must_use]
76 pub fn new_diff(
77 diff_result: DiffResult,
78 old_sbom: NormalizedSbom,
79 new_sbom: NormalizedSbom,
80 old_raw: &str,
81 new_raw: &str,
82 ) -> Self {
83 let old_profile =
85 crate::tui::scoring_profile_for(crate::model::BomProfile::detect(&old_sbom));
86 let old_scorer = QualityScorer::new(old_profile);
87 let old_quality = Some(old_scorer.score(&old_sbom));
88
89 let new_profile =
90 crate::tui::scoring_profile_for(crate::model::BomProfile::detect(&new_sbom));
91 let new_scorer = QualityScorer::new(new_profile);
92 let new_quality = Some(new_scorer.score(&new_sbom));
93
94 let old_cra_compliance =
96 Some(ComplianceChecker::new(ComplianceLevel::CraPhase2).check(&old_sbom));
97 let new_cra_compliance =
98 Some(ComplianceChecker::new(ComplianceLevel::CraPhase2).check(&new_sbom));
99
100 let old_sbom_index = Some(old_sbom.build_index());
102 let new_sbom_index = Some(new_sbom.build_index());
103
104 let mut app = Self::base(AppMode::Diff);
105 let mut source = SourceDiffState::new(old_raw, new_raw);
106 source.populate_annotations(&diff_result);
107 app.source_view = crate::tui::view_states::SourceView::with_state(source);
108 app.data.diff_result = Some(diff_result);
109 app.data.old_sbom = Some(old_sbom);
110 app.data.new_sbom = Some(new_sbom);
111 app.data.old_quality = old_quality;
112 app.data.new_quality = new_quality;
113 app.data.old_cra_compliance = old_cra_compliance;
114 app.data.new_cra_compliance = new_cra_compliance;
115 app.data.old_sbom_index = old_sbom_index;
116 app.data.new_sbom_index = new_sbom_index;
117 app
118 }
119
120 #[must_use]
125 pub fn with_cra_sidecar(mut self, sidecar: crate::model::CraSidecarMetadata) -> Self {
126 self.data.old_compliance_results = None;
127 self.data.new_compliance_results = None;
128 self.data.cra_sidecar = Some(sidecar);
129 self
130 }
131
132 #[must_use]
134 #[cfg(feature = "enrichment")]
135 pub fn with_enrichment_stats(
136 mut self,
137 old_stats: Option<crate::enrichment::EnrichmentStats>,
138 new_stats: Option<crate::enrichment::EnrichmentStats>,
139 ) -> Self {
140 self.data.enrichment_stats_old = old_stats;
141 self.data.enrichment_stats_new = new_stats;
142 self
143 }
144
145 #[cfg(feature = "enrichment")]
147 #[must_use]
148 pub fn combined_enrichment_stats(&self) -> Option<crate::enrichment::EnrichmentStats> {
149 match (
150 &self.data.enrichment_stats_old,
151 &self.data.enrichment_stats_new,
152 ) {
153 (Some(old), Some(new)) => {
154 let mut combined = old.clone();
155 combined.merge(new);
156 Some(combined)
157 }
158 (Some(stats), None) | (None, Some(stats)) => Some(stats.clone()),
159 (None, None) => None,
160 }
161 }
162
163 #[must_use]
168 pub fn new_view(sbom: NormalizedSbom, raw_content: &str) -> Self {
169 let profile = crate::tui::scoring_profile_for(crate::model::BomProfile::detect(&sbom));
171 let scorer = QualityScorer::new(profile);
172 let quality_report = Some(scorer.score(&sbom));
173
174 let sbom_index = Some(sbom.build_index());
176
177 let source = SourceDiffState::new("", raw_content);
179
180 let mut app = Self::base(AppMode::View);
181
182 app.active_tab = crate::config::TuiPreferences::load()
184 .last_view_tab
185 .as_deref()
186 .and_then(TabKind::from_str_opt)
187 .unwrap_or(TabKind::Overview);
188
189 app.source_view = crate::tui::view_states::SourceView::with_state(source);
190 app.data.sbom = Some(sbom);
191 app.data.sbom_index = sbom_index;
192 app.data.quality_report = quality_report;
193 app
194 }
195
196 #[must_use]
198 pub fn new_multi_diff(result: MultiDiffResult) -> Self {
199 let target_count = result.comparisons.len();
200
201 let mut app = Self::base(AppMode::MultiDiff);
202 app.data.multi_diff_result = Some(result);
203 app.tabs.multi_diff = MultiDiffState::new_with_targets(target_count);
204 app
205 }
206
207 #[must_use]
209 pub fn new_timeline(result: TimelineResult) -> Self {
210 let version_count = result.sboms.len();
211
212 let mut app = Self::base(AppMode::Timeline);
213 app.data.timeline_result = Some(result);
214 app.tabs.timeline = TimelineState::new_with_versions(version_count);
215 app
216 }
217
218 #[must_use]
220 pub fn new_matrix(result: MatrixResult) -> Self {
221 let sbom_count = result.sboms.len();
222
223 let mut app = Self::base(AppMode::Matrix);
224 app.data.matrix_result = Some(result);
225 app.tabs.matrix = MatrixState::new_with_size(sbom_count);
226 app
227 }
228}