1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! Foreground formatting without environment, color-choice or warning policy.
//! Callers choose whether to emit colors. Text is preserved verbatim, including
//! embedded escapes; this formatter is not a terminal-output sanitizer.
use std::{fmt, io};
/// Named colors in the ANSI 16-color palette (bright names match diagnostics).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Foreground {
Black = 0,
DarkRed = 1,
DarkGreen = 2,
DarkYellow = 3,
DarkBlue = 4,
DarkMagenta = 5,
DarkCyan = 6,
Grey = 7,
DarkGrey = 8,
Red = 9,
Green = 10,
Yellow = 11,
Blue = 12,
Magenta = 13,
Cyan = 14,
White = 15,
}
/// Borrowed, allocation-free text wrapper. Successful colored formatting
/// restores the default foreground, not a previously active foreground. If the
/// writer fails, its error is propagated; a reset cannot then be guaranteed.
#[derive(Clone, Copy, Debug)]
pub struct StyledText<'a> {
text: &'a str,
foreground: Foreground,
enabled: bool,
}
impl<'a> StyledText<'a> {
pub fn new(text: &'a str, foreground: Foreground, enabled: bool) -> Self {
Self {
text,
foreground,
enabled,
}
}
}
impl fmt::Display for StyledText<'_> {
fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.enabled {
write!(output, "\x1b[38;5;{}m", self.foreground as u8)?;
}
output.write_str(self.text)?;
if self.enabled {
output.write_str("\x1b[39m")?;
}
Ok(())
}
}
/// Prepare stderr's native console to interpret ANSI output, without deciding
/// color policy. Unix needs no preparation and returns true; this is not a TTY
/// or TERM probe. Windows returns false for redirected/non-console handles or
/// consoles without VT support, and errors for other native failures.
///
/// Windows mode changes last for the attached console buffer's lifetime and
/// can affect other writers sharing that buffer. Existing mode bits are kept.
/// Applications should prepare once, then decide how to handle NO_COLOR,
/// redirected output, TERM, and native errors themselves.
pub fn prepare_stderr_ansi() -> io::Result<bool> {
#[cfg(not(windows))]
{
Ok(true)
}
#[cfg(windows)]
{
use winapi::shared::winerror::{ERROR_INVALID_HANDLE, ERROR_INVALID_PARAMETER};
use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::processenv::GetStdHandle;
use winapi::um::winbase::STD_ERROR_HANDLE;
use winapi::um::wincon::{ENABLE_PROCESSED_OUTPUT, ENABLE_VIRTUAL_TERMINAL_PROCESSING};
// SAFETY: GetStdHandle has no pointer arguments; the borrowed handle is
// used only for mode calls and is never closed by this capability.
let handle = unsafe { GetStdHandle(STD_ERROR_HANDLE) };
if handle.is_null() || handle == INVALID_HANDLE_VALUE {
return Err(io::Error::new(
io::ErrorKind::NotConnected,
"stderr has no native handle",
));
}
let mut mode = 0;
// SAFETY: mode is writable; native code validates the borrowed handle.
if unsafe { GetConsoleMode(handle, &mut mode) } == 0 {
let error = io::Error::last_os_error();
return if error.raw_os_error() == Some(ERROR_INVALID_HANDLE as i32) {
Ok(false)
} else {
Err(error)
};
}
let enabled = mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
// SAFETY: the live console handle and documented output flags came from
// the successful mode query above. Other existing bits are preserved.
if mode != enabled && unsafe { SetConsoleMode(handle, enabled) } == 0 {
let error = io::Error::last_os_error();
return if error.raw_os_error() == Some(ERROR_INVALID_PARAMETER as i32) {
Ok(false)
} else {
Err(error)
};
}
Ok(true)
}
}