1use std::{io, io::IsTerminal, str::FromStr};
2
3use crate::{diagnostics::ShearAnalysis, output::miette::MietteRenderer};
4
5pub mod json;
6pub mod miette;
7
8#[derive(Debug, Clone, Copy, Default)]
10pub enum OutputFormat {
11 #[default]
13 Auto,
14
15 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#[derive(Debug, Clone, Copy, Default)]
33pub enum ColorMode {
34 #[default]
36 Auto,
37
38 Always,
40
41 Never,
43}
44
45impl ColorMode {
46 #[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}