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