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 #[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 #[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 Auto = 0,
46 Always = 1,
48 Never = 2,
50}
51
52impl OutputOptions {
54 pub fn init(&self) -> color_eyre::Result<()> {
55 self.color.set_global();
56 self.trace_init()
57 }
58}
59
60static 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}