inoculate/
term.rs

1use clap::{ArgGroup, Args};
2use std::{
3    fmt,
4    sync::atomic::{AtomicU8, Ordering},
5};
6
7pub const CARGO_LOG_WIDTH: usize = 12;
8pub use owo_colors::{style, OwoColorize, Style};
9const ARG_GROUP: &str = "output-opts";
10
11#[derive(Debug, Args)]
12#[command(
13    next_help_heading = "Output Options",
14    group = ArgGroup::new(ARG_GROUP).multiple(true),
15)]
16pub struct OutputOptions {
17    /// Whether to emit colors in output.
18    #[clap(
19            long,
20            env = "CARGO_TERM_COLORS",
21            default_value_t = ColorMode::Auto,
22            global = true,
23            group = ARG_GROUP,
24        )]
25    pub color: ColorMode,
26
27    /// Configures build logging.
28    #[clap(
29        short,
30        long,
31        env = "RUST_LOG",
32        default_value = "inoculate=info,warn",
33        global = true,
34        group = ARG_GROUP,
35    )]
36    pub log: tracing_subscriber::filter::Targets,
37}
38
39#[derive(Copy, Clone, Debug, Eq, PartialEq, clap::ValueEnum)]
40#[repr(u8)]
41#[clap(rename_all = "lower")]
42pub enum ColorMode {
43    /// Determine whether to color output based on whether or not stderr is a
44    /// TTY.
45    Auto = 0,
46    /// Always color output.
47    Always = 1,
48    /// Never color output.
49    Never = 2,
50}
51
52// === impl OutputOptions ===
53impl OutputOptions {
54    pub fn init(&self) -> color_eyre::Result<()> {
55        self.color.set_global();
56        self.trace_init()
57    }
58}
59
60// === impl ColorMode ===
61
62static GLOBAL_COLOR_MODE: AtomicU8 = AtomicU8::new(0);
63
64impl fmt::Display for ColorMode {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        f.pad(self.as_str())
67    }
68}
69
70impl ColorMode {
71    pub fn if_color(self, style: owo_colors::Style) -> owo_colors::Style {
72        if self.should_color_stderr() {
73            style
74        } else {
75            owo_colors::style()
76        }
77    }
78
79    pub fn set_global(self) {
80        GLOBAL_COLOR_MODE
81            .compare_exchange(0, self as u8, Ordering::AcqRel, Ordering::Acquire)
82            .expect("global color mode already set");
83    }
84
85    pub fn as_str(&self) -> &'static str {
86        match self {
87            ColorMode::Auto => "auto",
88            ColorMode::Always => "always",
89            ColorMode::Never => "never",
90        }
91    }
92
93    pub fn should_color_stdout(self) -> bool {
94        match self {
95            ColorMode::Auto => atty::is(atty::Stream::Stdout),
96            ColorMode::Always => true,
97            ColorMode::Never => false,
98        }
99    }
100
101    pub fn should_color_stderr(self) -> bool {
102        match self {
103            ColorMode::Auto => atty::is(atty::Stream::Stderr),
104            ColorMode::Always => true,
105            ColorMode::Never => false,
106        }
107    }
108}
109
110impl Default for ColorMode {
111    fn default() -> Self {
112        match GLOBAL_COLOR_MODE.load(Ordering::Acquire) {
113            1 => Self::Always,
114            2 => Self::Never,
115            _x => {
116                debug_assert_eq!(_x, 0, "weird color mode, what the heck?");
117                Self::Auto
118            }
119        }
120    }
121}