diagnostic/
windows.rs

1#[cfg(windows)]
2mod windows_console {
3    use std::os::raw::c_void;
4
5    #[allow(non_camel_case_types)]
6    type c_ulong = u32;
7    #[allow(non_camel_case_types)]
8    type c_int = i32;
9    type DWORD = c_ulong;
10    type LPDWORD = *mut DWORD;
11    type HANDLE = *mut c_void;
12    type BOOL = c_int;
13
14    const ENABLE_VIRTUAL_TERMINAL_PROCESSING: DWORD = 0x0004;
15    const STD_OUTPUT_HANDLE: DWORD = 0xFFFFFFF5;
16    const STD_ERROR_HANDLE: DWORD = 0xFFFFFFF4;
17    const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE;
18    const FALSE: BOOL = 0;
19    const TRUE: BOOL = 1;
20
21    // This is the win32 console API, taken from the 'winapi' crate.
22    extern "system" {
23        fn GetStdHandle(nStdHandle: DWORD) -> HANDLE;
24        fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL;
25        fn SetConsoleMode(hConsoleHandle: HANDLE, dwMode: DWORD) -> BOOL;
26    }
27
28    unsafe fn get_handle(handle_num: DWORD) -> Result<HANDLE, ()> {
29        match GetStdHandle(handle_num) {
30            handle if handle == INVALID_HANDLE_VALUE => Err(()),
31            handle => Ok(handle),
32        }
33    }
34
35    unsafe fn enable_vt(handle: HANDLE) -> Result<(), ()> {
36        let mut dw_mode: DWORD = 0;
37        if GetConsoleMode(handle, &mut dw_mode) == FALSE {
38            return Err(());
39        }
40
41        dw_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
42        match SetConsoleMode(handle, dw_mode) {
43            result if result == TRUE => Ok(()),
44            _ => Err(()),
45        }
46    }
47
48    unsafe fn enable_ascii_colors_raw() -> Result<bool, ()> {
49        let stdout_handle = get_handle(STD_OUTPUT_HANDLE)?;
50        let stderr_handle = get_handle(STD_ERROR_HANDLE)?;
51
52        enable_vt(stdout_handle)?;
53        if stdout_handle != stderr_handle {
54            enable_vt(stderr_handle)?;
55        }
56
57        Ok(true)
58    }
59
60    #[inline]
61    pub fn enable_ascii_colors() -> bool {
62        unsafe { enable_ascii_colors_raw().unwrap_or(false) }
63    }
64}
65
66#[cfg(not(windows))]
67mod windows_console {
68    pub fn enable_ascii_colors() -> bool {
69        true
70    }
71}
72
73/// Enables ASCII terminal escape sequences on Windows consoles when
74/// possible. Returns `true` if escape sequence support was successfully
75/// enabled and `false` otherwise. On non-Windows targets, this method
76/// always returns `true`.
77///
78/// Support for escape sequences in Windows consoles was added in the
79/// Windows 10 anniversary update. For targets with older Windows
80/// installations, this method is expected to return `false`.
81///
82/// # Example
83///
84/// ```rust
85/// use diagnostic::enable_ansi_color;
86///
87/// // A best-effort Windows ASCII terminal support enabling.
88/// enable_ansi_color();
89/// ```
90#[inline]
91pub fn enable_ansi_color() -> bool {
92    match std::env::var("DIAGNOSTIC_COLOR") {
93        Ok(o) if accept(&o) => windows_console::enable_ascii_colors(),
94        _ => false,
95    }
96}
97
98fn accept(s: &str) -> bool {
99    if s.eq("1") {
100        true
101    }
102    else if s.eq_ignore_ascii_case("true") {
103        true
104    }
105    else {
106        false
107    }
108}