#[cfg(windows)]
use std::ffi::c_void;
pub fn init_terminal() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(windows)]
{
init_windows_terminal()?;
}
#[cfg(not(windows))]
{
}
Ok(())
}
#[cfg(windows)]
fn init_windows_terminal() -> Result<(), Box<dyn std::error::Error>> {
const STD_OUTPUT_HANDLE: u32 = 0xFFFFFFF5_u32; const STD_ERROR_HANDLE: u32 = 0xFFFFFFF4_u32; const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 0x0004;
const ENABLE_PROCESSED_OUTPUT: u32 = 0x0001;
#[link(name = "kernel32")]
extern "system" {
fn GetStdHandle(nStdHandle: u32) -> *mut c_void;
fn GetConsoleMode(hConsoleHandle: *mut c_void, lpMode: *mut u32) -> i32;
fn SetConsoleMode(hConsoleHandle: *mut c_void, dwMode: u32) -> i32;
}
unsafe {
let stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
if !stdout_handle.is_null() {
let mut mode: u32 = 0;
if GetConsoleMode(stdout_handle, &mut mode) != 0 {
let new_mode = mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
if SetConsoleMode(stdout_handle, new_mode) == 0 {
eprintln!("警告: 无法为标准输出启用 ANSI 颜色支持");
}
}
}
let stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
if !stderr_handle.is_null() {
let mut mode: u32 = 0;
if GetConsoleMode(stderr_handle, &mut mode) != 0 {
let new_mode = mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
if SetConsoleMode(stderr_handle, new_mode) == 0 {
eprintln!("警告: 无法为标准错误输出启用 ANSI 颜色支持");
}
}
}
}
Ok(())
}
pub fn supports_color() -> bool {
if !atty::is(atty::Stream::Stdout) {
return false;
}
if let Ok(term) = std::env::var("TERM") {
if term == "dumb" {
return false;
}
}
if std::env::var("NO_COLOR").is_ok() {
return false;
}
if std::env::var("FORCE_COLOR").is_ok() {
return true;
}
#[cfg(windows)]
{
return check_windows_color_support();
}
#[cfg(not(windows))]
{
true
}
}
#[cfg(windows)]
fn check_windows_color_support() -> bool {
const STD_OUTPUT_HANDLE: u32 = 0xFFFFFFF5_u32; const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 0x0004;
#[link(name = "kernel32")]
extern "system" {
fn GetStdHandle(nStdHandle: u32) -> *mut c_void;
fn GetConsoleMode(hConsoleHandle: *mut c_void, lpMode: *mut u32) -> i32;
}
unsafe {
let stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
if !stdout_handle.is_null() {
let mut mode: u32 = 0;
if GetConsoleMode(stdout_handle, &mut mode) != 0 {
return (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0;
}
}
}
false
}
#[derive(Debug, Clone)]
pub struct ColorConfig {
pub force_color: bool,
pub no_color: bool,
pub auto_detect: bool,
}
impl Default for ColorConfig {
fn default() -> Self {
Self {
force_color: false,
no_color: false,
auto_detect: true,
}
}
}
impl ColorConfig {
pub fn should_use_color(&self) -> bool {
if self.no_color {
return false;
}
if self.force_color {
return true;
}
if self.auto_detect {
return supports_color();
}
false
}
pub fn from_env() -> Self {
let force_color = std::env::var("FORCE_COLOR").is_ok();
let no_color = std::env::var("NO_COLOR").is_ok();
Self {
force_color,
no_color,
auto_detect: !force_color && !no_color,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color_config_default() {
let config = ColorConfig::default();
assert!(!config.force_color);
assert!(!config.no_color);
assert!(config.auto_detect);
}
#[test]
fn test_color_config_force_color() {
let config = ColorConfig {
force_color: true,
no_color: false,
auto_detect: false,
};
assert!(config.should_use_color());
}
#[test]
fn test_color_config_no_color() {
let config = ColorConfig {
force_color: false,
no_color: true,
auto_detect: false,
};
assert!(!config.should_use_color());
}
#[test]
fn test_color_config_no_color_overrides_force() {
let config = ColorConfig {
force_color: true,
no_color: true,
auto_detect: false,
};
assert!(!config.should_use_color());
}
}