cargo_shear/
output.rs

1use std::{io, io::IsTerminal, str::FromStr};
2
3use crate::{diagnostics::ShearAnalysis, output::miette::MietteRenderer};
4
5pub mod json;
6pub mod miette;
7
8/// Output format for cargo-shear.
9#[derive(Debug, Clone, Copy, Default)]
10pub enum OutputFormat {
11    /// Auto format with colors and unicode.
12    #[default]
13    Auto,
14
15    /// JSON format for machine-readable output.
16    Json,
17}
18
19impl FromStr for OutputFormat {
20    type Err = String;
21
22    fn from_str(s: &str) -> Result<Self, Self::Err> {
23        match s.to_lowercase().as_str() {
24            "auto" => Ok(Self::Auto),
25            "json" => Ok(Self::Json),
26            _ => Err(format!("unknown format: {s}, expected: auto, json")),
27        }
28    }
29}
30
31/// Color mode for output.
32#[derive(Debug, Clone, Copy, Default)]
33pub enum ColorMode {
34    /// Automatically detect based on environment.
35    #[default]
36    Auto,
37
38    /// Always use colors.
39    Always,
40
41    /// Never use colors.
42    Never,
43}
44
45impl ColorMode {
46    /// Whether to show color.
47    #[must_use]
48    pub fn enabled(self) -> bool {
49        match self {
50            Self::Always => true,
51            Self::Never => false,
52            Self::Auto => {
53                if std::env::var_os("NO_COLOR").is_some() {
54                    return false;
55                }
56
57                std::io::stdout().is_terminal()
58            }
59        }
60    }
61}
62
63impl FromStr for ColorMode {
64    type Err = String;
65
66    fn from_str(s: &str) -> Result<Self, Self::Err> {
67        match s.to_lowercase().as_str() {
68            "auto" => Ok(Self::Auto),
69            "always" => Ok(Self::Always),
70            "never" => Ok(Self::Never),
71            _ => Err(format!("unknown color option: {s}, expected one of: auto, always, never")),
72        }
73    }
74}
75
76pub struct Renderer<W> {
77    writer: W,
78    format: OutputFormat,
79    color: bool,
80}
81
82impl<W: io::Write> Renderer<W> {
83    pub const fn new(writer: W, format: OutputFormat, color: bool) -> Self {
84        Self { writer, format, color }
85    }
86
87    pub fn render(&mut self, analysis: &ShearAnalysis) -> io::Result<()> {
88        match self.format {
89            OutputFormat::Auto => {
90                let mut renderer = MietteRenderer::new(&mut self.writer, self.color);
91                renderer.render(analysis)
92            }
93            OutputFormat::Json => {
94                let mut renderer = json::JsonRenderer::new(&mut self.writer);
95                renderer.render(analysis)
96            }
97        }
98    }
99}