Skip to main content

sbom_tools/reports/
types.rs

1//! Report type definitions.
2
3use clap::ValueEnum;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7/// Output format for reports
8#[derive(
9    Debug, Clone, Copy, Default, PartialEq, Eq, ValueEnum, Serialize, Deserialize, JsonSchema,
10)]
11#[non_exhaustive]
12pub enum ReportFormat {
13    /// Auto-detect: TUI if TTY, summary otherwise
14    #[default]
15    Auto,
16    /// Interactive TUI display
17    Tui,
18    /// Side-by-side terminal diff (like difftastic)
19    #[value(alias = "side-by-side")]
20    SideBySide,
21    /// Structured JSON output
22    Json,
23    /// SARIF 2.1.0 for CI/CD
24    Sarif,
25    /// Human-readable Markdown
26    Markdown,
27    /// Interactive HTML report
28    Html,
29    /// Brief summary output
30    Summary,
31    /// Compact table for terminal (colored)
32    Table,
33    /// CSV for spreadsheet import
34    Csv,
35}
36
37impl std::fmt::Display for ReportFormat {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        match self {
40            Self::Auto => write!(f, "auto"),
41            Self::Tui => write!(f, "tui"),
42            Self::SideBySide => write!(f, "side-by-side"),
43            Self::Json => write!(f, "json"),
44            Self::Sarif => write!(f, "sarif"),
45            Self::Markdown => write!(f, "markdown"),
46            Self::Html => write!(f, "html"),
47            Self::Summary => write!(f, "summary"),
48            Self::Table => write!(f, "table"),
49            Self::Csv => write!(f, "csv"),
50        }
51    }
52}
53
54/// Types of reports that can be generated
55#[derive(
56    Debug, Clone, Copy, Default, PartialEq, Eq, ValueEnum, Serialize, Deserialize, JsonSchema,
57)]
58pub enum ReportType {
59    /// All report types
60    #[default]
61    All,
62    /// Component changes summary
63    Components,
64    /// Dependency changes
65    Dependencies,
66    /// OSS dependency changes
67    OssDependencies,
68    /// License changes
69    Licenses,
70    /// Vulnerability changes
71    Vulnerabilities,
72}
73
74/// Minimum severity level for filtering
75#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
76pub enum MinSeverity {
77    Low,
78    Medium,
79    High,
80    Critical,
81}
82
83impl MinSeverity {
84    /// Parse severity from string. Returns None for unrecognized values.
85    #[must_use]
86    pub fn parse(s: &str) -> Option<Self> {
87        match s.to_lowercase().as_str() {
88            "low" => Some(Self::Low),
89            "medium" => Some(Self::Medium),
90            "high" => Some(Self::High),
91            "critical" => Some(Self::Critical),
92            _ => None,
93        }
94    }
95
96    /// Check if a severity string meets this minimum threshold
97    #[must_use]
98    pub fn meets_threshold(&self, severity: &str) -> bool {
99        let sev = match severity.to_lowercase().as_str() {
100            "critical" => Self::Critical,
101            "high" => Self::High,
102            "medium" => Self::Medium,
103            "low" => Self::Low,
104            _ => return true, // Unknown severities are included
105        };
106        sev >= *self
107    }
108}
109
110/// Configuration for report generation
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct ReportConfig {
113    /// Which report types to include
114    pub report_types: Vec<ReportType>,
115    /// Include unchanged items in the report
116    pub include_unchanged: bool,
117    /// Maximum items per section
118    pub max_items: Option<usize>,
119    /// Include detailed field changes
120    pub include_field_changes: bool,
121    /// Title for the report
122    pub title: Option<String>,
123    /// Additional metadata to include
124    pub metadata: ReportMetadata,
125    /// Only show items with changes (filter out unchanged)
126    pub only_changes: bool,
127    /// Minimum severity level for vulnerability filtering
128    pub min_severity: Option<MinSeverity>,
129    /// Pre-computed CRA compliance for old SBOM (avoids redundant recomputation)
130    #[serde(skip)]
131    pub old_cra_compliance: Option<crate::quality::ComplianceResult>,
132    /// Pre-computed CRA compliance for new SBOM (avoids redundant recomputation)
133    #[serde(skip)]
134    pub new_cra_compliance: Option<crate::quality::ComplianceResult>,
135    /// Pre-computed CRA compliance for single SBOM in view mode
136    #[serde(skip)]
137    pub view_cra_compliance: Option<crate::quality::ComplianceResult>,
138}
139
140impl Default for ReportConfig {
141    fn default() -> Self {
142        Self {
143            report_types: vec![ReportType::All],
144            include_unchanged: false,
145            max_items: None,
146            include_field_changes: true,
147            title: None,
148            metadata: ReportMetadata::default(),
149            only_changes: false,
150            min_severity: None,
151            old_cra_compliance: None,
152            new_cra_compliance: None,
153            view_cra_compliance: None,
154        }
155    }
156}
157
158impl ReportConfig {
159    /// Create a config for all report types
160    #[must_use]
161    pub fn all() -> Self {
162        Self::default()
163    }
164
165    /// Create a config for specific report types
166    #[must_use]
167    pub fn with_types(types: Vec<ReportType>) -> Self {
168        Self {
169            report_types: types,
170            ..Default::default()
171        }
172    }
173
174    /// Check if a report type should be included
175    #[must_use]
176    pub fn includes(&self, report_type: ReportType) -> bool {
177        self.report_types.contains(&ReportType::All) || self.report_types.contains(&report_type)
178    }
179}
180
181/// Metadata included in reports
182#[derive(Debug, Clone, Default, Serialize, Deserialize)]
183pub struct ReportMetadata {
184    /// Old SBOM file path
185    pub old_sbom_path: Option<String>,
186    /// New SBOM file path
187    pub new_sbom_path: Option<String>,
188    /// Tool version
189    pub tool_version: String,
190    /// Generation timestamp
191    pub generated_at: Option<String>,
192    /// Custom properties
193    pub custom: std::collections::HashMap<String, String>,
194}
195
196impl ReportMetadata {
197    #[must_use]
198    pub fn new() -> Self {
199        Self {
200            tool_version: env!("CARGO_PKG_VERSION").to_string(),
201            ..Default::default()
202        }
203    }
204}