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