sbom_tools/pipeline/
output.rs1use crate::reports::ReportFormat;
6use anyhow::{Context, Result};
7use std::io::IsTerminal;
8use std::path::PathBuf;
9
10#[derive(Debug, Clone)]
12pub enum OutputTarget {
13 Stdout,
15 File(PathBuf),
17}
18
19impl OutputTarget {
20 pub fn from_option(path: Option<PathBuf>) -> Self {
22 match path {
23 Some(p) => OutputTarget::File(p),
24 None => OutputTarget::Stdout,
25 }
26 }
27
28 pub fn is_terminal(&self) -> bool {
30 matches!(self, OutputTarget::Stdout) && std::io::stdout().is_terminal()
31 }
32}
33
34pub fn auto_detect_format(format: ReportFormat, target: &OutputTarget) -> ReportFormat {
39 match format {
40 ReportFormat::Auto => {
41 if target.is_terminal() {
42 ReportFormat::Tui
43 } else {
44 ReportFormat::Summary
45 }
46 }
47 other => other,
48 }
49}
50
51pub fn should_use_color(no_color_flag: bool) -> bool {
53 !no_color_flag && std::env::var("NO_COLOR").is_err()
54}
55
56pub fn write_output(content: &str, target: &OutputTarget, quiet: bool) -> Result<()> {
58 match target {
59 OutputTarget::Stdout => {
60 println!("{}", content);
61 Ok(())
62 }
63 OutputTarget::File(path) => {
64 std::fs::write(path, content)
65 .with_context(|| format!("Failed to write output to {:?}", path))?;
66 if !quiet {
67 tracing::info!("Report written to {:?}", path);
68 }
69 Ok(())
70 }
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn test_output_target_from_option_none() {
80 let target = OutputTarget::from_option(None);
81 assert!(matches!(target, OutputTarget::Stdout));
82 }
83
84 #[test]
85 fn test_output_target_from_option_some() {
86 let path = PathBuf::from("/tmp/test.json");
87 let target = OutputTarget::from_option(Some(path.clone()));
88 match target {
89 OutputTarget::File(p) => assert_eq!(p, path),
90 _ => panic!("Expected File variant"),
91 }
92 }
93
94 #[test]
95 fn test_auto_detect_format_non_auto() {
96 let target = OutputTarget::Stdout;
97 assert_eq!(
98 auto_detect_format(ReportFormat::Json, &target),
99 ReportFormat::Json
100 );
101 assert_eq!(
102 auto_detect_format(ReportFormat::Sarif, &target),
103 ReportFormat::Sarif
104 );
105 }
106
107 #[test]
108 fn test_auto_detect_format_file_target() {
109 let target = OutputTarget::File(PathBuf::from("/tmp/test.json"));
110 assert_eq!(
112 auto_detect_format(ReportFormat::Auto, &target),
113 ReportFormat::Summary
114 );
115 }
116
117 #[test]
118 fn test_should_use_color_with_flag() {
119 assert!(!should_use_color(true));
120 }
121
122 #[test]
123 fn test_should_use_color_without_flag() {
124 let expected = std::env::var("NO_COLOR").is_err();
126 assert_eq!(should_use_color(false), expected);
127 }
128}