mod diff_stage;
pub mod enrich;
mod output;
mod parse;
mod report_stage;
pub use diff_stage::compute_diff;
pub use enrich::{AggregatedEnrichmentStats, enrich_sbom_full, enrich_sboms};
pub use output::{OutputTarget, auto_detect_format, should_use_color, write_output};
pub use parse::{ParsedSbom, parse_sbom_with_context};
pub use report_stage::output_report;
#[cfg(feature = "enrichment")]
pub use parse::{build_enrichment_config, enrich_eol, enrich_sbom, enrich_vex};
#[derive(Debug, thiserror::Error)]
pub enum PipelineError {
#[error("Parse failed for {path}: {source}")]
ParseFailed { path: String, source: anyhow::Error },
#[error("Enrichment failed: {reason}")]
EnrichmentFailed { reason: String },
#[error("Diff failed: {source}")]
DiffFailed {
#[source]
source: anyhow::Error,
},
#[error("Report failed: {source}")]
ReportFailed {
#[source]
source: anyhow::Error,
},
}
pub mod exit_codes {
pub const SUCCESS: i32 = 0;
pub const CHANGES_DETECTED: i32 = 1;
pub const VULNS_INTRODUCED: i32 = 2;
pub const ERROR: i32 = 3;
pub const VEX_GAPS_FOUND: i32 = 4;
pub const LICENSE_VIOLATIONS: i32 = 5;
}
pub mod dirs {
use std::path::PathBuf;
#[must_use]
pub fn cache_dir() -> Option<PathBuf> {
#[cfg(target_os = "macos")]
{
std::env::var("HOME")
.ok()
.map(|h| PathBuf::from(h).join("Library").join("Caches"))
}
#[cfg(target_os = "linux")]
{
std::env::var("XDG_CACHE_HOME")
.ok()
.map(PathBuf::from)
.or_else(|| {
std::env::var("HOME")
.ok()
.map(|h| PathBuf::from(h).join(".cache"))
})
}
#[cfg(target_os = "windows")]
{
std::env::var("LOCALAPPDATA").ok().map(PathBuf::from)
}
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
{
std::env::var("HOME")
.ok()
.map(|h| PathBuf::from(h).join(".cache"))
}
}
#[must_use]
pub fn osv_cache_dir() -> PathBuf {
cache_dir()
.unwrap_or_else(|| PathBuf::from(".cache"))
.join("sbom-tools")
.join("osv")
}
#[must_use]
pub fn eol_cache_dir() -> PathBuf {
cache_dir()
.unwrap_or_else(|| PathBuf::from(".cache"))
.join("sbom-tools")
.join("eol")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exit_codes_values() {
assert_eq!(exit_codes::SUCCESS, 0);
assert_eq!(exit_codes::CHANGES_DETECTED, 1);
assert_eq!(exit_codes::VULNS_INTRODUCED, 2);
assert_eq!(exit_codes::ERROR, 3);
assert_eq!(exit_codes::VEX_GAPS_FOUND, 4);
}
#[test]
fn test_cache_dir_returns_some() {
let _ = dirs::cache_dir();
}
#[test]
fn test_osv_cache_dir_path() {
let path = dirs::osv_cache_dir();
let path_str = path.to_string_lossy();
assert!(path_str.contains("osv"));
}
}