1use crate::diff::{DiffResult, MatrixResult, MultiDiffResult, TimelineResult};
4use crate::model::NormalizedSbom;
5use crate::quality::{ComplianceChecker, ComplianceLevel, QualityScorer, ScoringProfile};
6use ratatui::widgets::ScrollbarState;
7
8use super::app::{App, AppMode, AppOverlays, DataContext, TabKind, TabStates};
9use super::app_states::{
10 ComponentsState, DependenciesState, GraphChangesState, LicensesState,
11 MatrixState, MultiDiffState, NavigationContext, QualityState,
12 SideBySideState, SourceDiffState, TimelineState, VulnerabilitiesState,
13};
14
15impl App {
16 fn base(mode: AppMode, components_len: usize, vulns_len: usize) -> Self {
19 Self {
20 mode,
21 active_tab: 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 matching_threshold: 0.85,
41 #[cfg(feature = "enrichment")]
42 enrichment_stats_old: None,
43 #[cfg(feature = "enrichment")]
44 enrichment_stats_new: None,
45 },
46 tabs: TabStates {
47 components: ComponentsState::new(components_len),
48 dependencies: DependenciesState::new(),
49 licenses: LicensesState::new(),
50 vulnerabilities: VulnerabilitiesState::new(vulns_len),
51 quality: QualityState::new(),
52 graph_changes: GraphChangesState::new(),
53 side_by_side: SideBySideState::new(),
54 diff_compliance: crate::tui::app_states::DiffComplianceState::new(),
55 multi_diff: MultiDiffState::new(),
56 timeline: TimelineState::new(),
57 matrix: MatrixState::new(),
58 source: SourceDiffState::new("", ""),
59 },
60 overlays: AppOverlays::new(),
61 scroll_state: ScrollbarState::default(),
62 should_quit: false,
63 status_message: None,
64 tick: 0,
65 last_export_path: None,
66 navigation_ctx: NavigationContext::new(),
67 security_cache: crate::tui::security::SecurityAnalysisCache::new(),
68 compliance_state: crate::tui::app_states::PolicyComplianceState::new(),
69 quality_view: Some(crate::tui::view_states::QualityView::new()),
70 }
71 }
72
73 pub fn new_diff(
75 diff_result: DiffResult,
76 old_sbom: NormalizedSbom,
77 new_sbom: NormalizedSbom,
78 old_raw: String,
79 new_raw: String,
80 ) -> Self {
81 let components_len = diff_result.components.total();
82 let vulns_len = diff_result.vulnerabilities.introduced.len()
83 + diff_result.vulnerabilities.resolved.len()
84 + diff_result.vulnerabilities.persistent.len();
85
86 let scorer = QualityScorer::new(ScoringProfile::Standard);
88 let old_quality = Some(scorer.score(&old_sbom));
89 let new_quality = Some(scorer.score(&new_sbom));
90
91 let old_cra_compliance = Some(ComplianceChecker::new(ComplianceLevel::CraPhase2).check(&old_sbom));
93 let new_cra_compliance = Some(ComplianceChecker::new(ComplianceLevel::CraPhase2).check(&new_sbom));
94
95 let old_sbom_index = Some(old_sbom.build_index());
97 let new_sbom_index = Some(new_sbom.build_index());
98
99 let mut app = Self::base(AppMode::Diff, components_len, vulns_len);
100 app.tabs.source = SourceDiffState::new(&old_raw, &new_raw);
101 app.data.diff_result = Some(diff_result);
102 app.data.old_sbom = Some(old_sbom);
103 app.data.new_sbom = Some(new_sbom);
104 app.data.old_quality = old_quality;
105 app.data.new_quality = new_quality;
106 app.data.old_cra_compliance = old_cra_compliance;
107 app.data.new_cra_compliance = new_cra_compliance;
108 app.data.old_sbom_index = old_sbom_index;
109 app.data.new_sbom_index = new_sbom_index;
110 app
111 }
112
113 #[cfg(feature = "enrichment")]
115 pub fn with_enrichment_stats(
116 mut self,
117 old_stats: Option<crate::enrichment::EnrichmentStats>,
118 new_stats: Option<crate::enrichment::EnrichmentStats>,
119 ) -> Self {
120 self.data.enrichment_stats_old = old_stats;
121 self.data.enrichment_stats_new = new_stats;
122 self
123 }
124
125 #[cfg(feature = "enrichment")]
127 pub fn combined_enrichment_stats(&self) -> Option<crate::enrichment::EnrichmentStats> {
128 match (&self.data.enrichment_stats_old, &self.data.enrichment_stats_new) {
129 (Some(old), Some(new)) => {
130 let mut combined = old.clone();
131 combined.merge(new);
132 Some(combined)
133 }
134 (Some(stats), None) | (None, Some(stats)) => Some(stats.clone()),
135 (None, None) => None,
136 }
137 }
138
139 pub fn new_view(sbom: NormalizedSbom) -> Self {
141 let components_len = sbom.component_count();
142 let vulns_len = sbom.all_vulnerabilities().len();
143
144 let scorer = QualityScorer::new(ScoringProfile::Standard);
146 let quality_report = Some(scorer.score(&sbom));
147
148 let sbom_index = Some(sbom.build_index());
150
151 let mut app = Self::base(AppMode::View, components_len, vulns_len);
152 app.data.sbom = Some(sbom);
153 app.data.quality_report = quality_report;
154 app.data.sbom_index = sbom_index;
155 app
156 }
157
158 pub fn new_multi_diff(result: MultiDiffResult) -> Self {
160 let target_count = result.comparisons.len();
161
162 let mut app = Self::base(AppMode::MultiDiff, 0, 0);
163 app.data.multi_diff_result = Some(result);
164 app.tabs.multi_diff = MultiDiffState::new_with_targets(target_count);
165 app
166 }
167
168 pub fn new_timeline(result: TimelineResult) -> Self {
170 let version_count = result.sboms.len();
171
172 let mut app = Self::base(AppMode::Timeline, 0, 0);
173 app.data.timeline_result = Some(result);
174 app.tabs.timeline = TimelineState::new_with_versions(version_count);
175 app
176 }
177
178 pub fn new_matrix(result: MatrixResult) -> Self {
180 let sbom_count = result.sboms.len();
181
182 let mut app = Self::base(AppMode::Matrix, 0, 0);
183 app.data.matrix_result = Some(result);
184 app.tabs.matrix = MatrixState::new_with_size(sbom_count);
185 app
186 }
187}