1use super::app::{App, AppMode, AppOverlays, DataContext, TabKind, TabStates};
4use super::app_states::{
5 ComponentsState, DependenciesState, GraphChangesState, LicensesState, MatrixState,
6 MultiDiffState, NavigationContext, QualityState, SideBySideState, SourceDiffState,
7 TimelineState, VulnerabilitiesState,
8};
9use crate::diff::{DiffResult, MatrixResult, MultiDiffResult, TimelineResult};
10use crate::model::NormalizedSbom;
11use crate::quality::{ComplianceChecker, ComplianceLevel, QualityScorer, ScoringProfile};
12
13impl App {
14 fn base(mode: AppMode, components_len: usize, vulns_len: usize) -> Self {
17 Self {
18 mode,
19 active_tab: crate::config::TuiPreferences::load()
20 .last_tab
21 .as_deref()
22 .and_then(TabKind::from_str_opt)
23 .unwrap_or(TabKind::Summary),
24 data: DataContext {
25 diff_result: None,
26 old_sbom: None,
27 new_sbom: None,
28 sbom: None,
29 multi_diff_result: None,
30 timeline_result: None,
31 matrix_result: None,
32 old_sbom_index: None,
33 new_sbom_index: None,
34 sbom_index: None,
35 old_quality: None,
36 new_quality: None,
37 quality_report: None,
38 old_cra_compliance: None,
39 new_cra_compliance: None,
40 old_compliance_results: None,
41 new_compliance_results: None,
42 matching_threshold: 0.85,
43 #[cfg(feature = "enrichment")]
44 enrichment_stats_old: None,
45 #[cfg(feature = "enrichment")]
46 enrichment_stats_new: None,
47 },
48 tabs: TabStates {
49 components: ComponentsState::new(components_len),
50 dependencies: DependenciesState::new(),
51 licenses: LicensesState::new(),
52 vulnerabilities: VulnerabilitiesState::new(vulns_len),
53 quality: QualityState::new(),
54 graph_changes: GraphChangesState::new(),
55 side_by_side: SideBySideState::new(),
56 diff_compliance: crate::tui::app_states::DiffComplianceState::new(),
57 multi_diff: MultiDiffState::new(),
58 timeline: TimelineState::new(),
59 matrix: MatrixState::new(),
60 source: SourceDiffState::new("", ""),
61 },
62 overlays: AppOverlays::new(),
63 should_quit: false,
64 status_message: None,
65 status_sticky: false,
66 tick: 0,
67 last_export_path: None,
68 navigation_ctx: NavigationContext::new(),
69 security_cache: crate::tui::security::SecurityAnalysisCache::new(),
70 compliance_state: crate::tui::app_states::PolicyComplianceState::new(),
71 export_template: None,
72 quality_view: Some(crate::tui::view_states::QualityView::new()),
73 }
74 }
75
76 #[must_use]
78 pub fn new_diff(
79 diff_result: DiffResult,
80 old_sbom: NormalizedSbom,
81 new_sbom: NormalizedSbom,
82 old_raw: &str,
83 new_raw: &str,
84 ) -> Self {
85 let components_len = diff_result.components.total();
86 let vulns_len = diff_result.vulnerabilities.introduced.len()
87 + diff_result.vulnerabilities.resolved.len()
88 + diff_result.vulnerabilities.persistent.len();
89
90 let scorer = QualityScorer::new(ScoringProfile::Standard);
92 let old_quality = Some(scorer.score(&old_sbom));
93 let new_quality = Some(scorer.score(&new_sbom));
94
95 let old_cra_compliance =
97 Some(ComplianceChecker::new(ComplianceLevel::CraPhase2).check(&old_sbom));
98 let new_cra_compliance =
99 Some(ComplianceChecker::new(ComplianceLevel::CraPhase2).check(&new_sbom));
100
101 let old_sbom_index = Some(old_sbom.build_index());
103 let new_sbom_index = Some(new_sbom.build_index());
104
105 let mut app = Self::base(AppMode::Diff, components_len, vulns_len);
106 app.tabs.source = SourceDiffState::new(old_raw, new_raw);
107 app.tabs.source.populate_annotations(&diff_result);
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]
122 #[cfg(feature = "enrichment")]
123 pub fn with_enrichment_stats(
124 mut self,
125 old_stats: Option<crate::enrichment::EnrichmentStats>,
126 new_stats: Option<crate::enrichment::EnrichmentStats>,
127 ) -> Self {
128 self.data.enrichment_stats_old = old_stats;
129 self.data.enrichment_stats_new = new_stats;
130 self
131 }
132
133 #[cfg(feature = "enrichment")]
135 #[must_use]
136 pub fn combined_enrichment_stats(&self) -> Option<crate::enrichment::EnrichmentStats> {
137 match (
138 &self.data.enrichment_stats_old,
139 &self.data.enrichment_stats_new,
140 ) {
141 (Some(old), Some(new)) => {
142 let mut combined = old.clone();
143 combined.merge(new);
144 Some(combined)
145 }
146 (Some(stats), None) | (None, Some(stats)) => Some(stats.clone()),
147 (None, None) => None,
148 }
149 }
150
151 #[must_use]
153 pub fn new_view(sbom: NormalizedSbom) -> Self {
154 let components_len = sbom.component_count();
155 let vulns_len = sbom.all_vulnerabilities().len();
156
157 let scorer = QualityScorer::new(ScoringProfile::Standard);
159 let quality_report = Some(scorer.score(&sbom));
160
161 let sbom_index = Some(sbom.build_index());
163
164 let mut app = Self::base(AppMode::View, components_len, vulns_len);
165 app.data.sbom = Some(sbom);
166 app.data.quality_report = quality_report;
167 app.data.sbom_index = sbom_index;
168 app
169 }
170
171 #[must_use]
173 pub fn new_multi_diff(result: MultiDiffResult) -> Self {
174 let target_count = result.comparisons.len();
175
176 let mut app = Self::base(AppMode::MultiDiff, 0, 0);
177 app.data.multi_diff_result = Some(result);
178 app.tabs.multi_diff = MultiDiffState::new_with_targets(target_count);
179 app
180 }
181
182 #[must_use]
184 pub fn new_timeline(result: TimelineResult) -> Self {
185 let version_count = result.sboms.len();
186
187 let mut app = Self::base(AppMode::Timeline, 0, 0);
188 app.data.timeline_result = Some(result);
189 app.tabs.timeline = TimelineState::new_with_versions(version_count);
190 app
191 }
192
193 #[must_use]
195 pub fn new_matrix(result: MatrixResult) -> Self {
196 let sbom_count = result.sboms.len();
197
198 let mut app = Self::base(AppMode::Matrix, 0, 0);
199 app.data.matrix_result = Some(result);
200 app.tabs.matrix = MatrixState::new_with_size(sbom_count);
201 app
202 }
203}