Enhance CLI output: UTF-8 console codepage, VT colors, clean status cards
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
static VT_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// Initialisiert die Windows-Konsole:
|
||||
/// - Setzt Code-Page 65001 (UTF-8) für Ein- und Ausgabe (verhindert Umlaut-Fehler wie `f├╝r`)
|
||||
/// - Aktiviert VIRTUAL_TERMINAL_PROCESSING für saubere ANSI-Farb- und Escape-Sequenz-Darstellung
|
||||
#[cfg(windows)]
|
||||
pub fn init_console() {
|
||||
const CP_UTF8: u32 = 65001;
|
||||
const STD_OUTPUT_HANDLE: u32 = 0xFFFFFFF5; // (DWORD)-11
|
||||
const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 0x0004;
|
||||
|
||||
extern "system" {
|
||||
fn SetConsoleOutputCP(wCodePageID: u32) -> i32;
|
||||
fn SetConsoleCP(wCodePageID: u32) -> i32;
|
||||
fn GetStdHandle(nStdHandle: u32) -> *mut std::ffi::c_void;
|
||||
fn GetConsoleMode(hConsoleHandle: *mut std::ffi::c_void, lpMode: *mut u32) -> i32;
|
||||
fn SetConsoleMode(hConsoleHandle: *mut std::ffi::c_void, dwMode: u32) -> i32;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _ = SetConsoleOutputCP(CP_UTF8);
|
||||
let _ = SetConsoleCP(CP_UTF8);
|
||||
|
||||
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if !handle.is_null() && handle as isize != -1 {
|
||||
let mut mode: u32 = 0;
|
||||
if GetConsoleMode(handle, &mut mode) != 0 {
|
||||
if SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0 {
|
||||
VT_ENABLED.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub fn init_console() {
|
||||
VT_ENABLED.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn is_vt_enabled() -> bool {
|
||||
VT_ENABLED.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn bold(s: &str) -> String {
|
||||
if is_vt_enabled() {
|
||||
format!("\x1b[1m{s}\x1b[0m")
|
||||
} else {
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dim(s: &str) -> String {
|
||||
if is_vt_enabled() {
|
||||
format!("\x1b[90m{s}\x1b[0m")
|
||||
} else {
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn green(s: &str) -> String {
|
||||
if is_vt_enabled() {
|
||||
format!("\x1b[1;32m{s}\x1b[0m")
|
||||
} else {
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cyan(s: &str) -> String {
|
||||
if is_vt_enabled() {
|
||||
format!("\x1b[1;36m{s}\x1b[0m")
|
||||
} else {
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn yellow(s: &str) -> String {
|
||||
if is_vt_enabled() {
|
||||
format!("\x1b[1;33m{s}\x1b[0m")
|
||||
} else {
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn red(s: &str) -> String {
|
||||
if is_vt_enabled() {
|
||||
format!("\x1b[1;31m{s}\x1b[0m")
|
||||
} else {
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Gibt einen formatierten Fortschrittsschritt aus: ` [1/4] 📦 Schrittbeschreibung...`
|
||||
pub fn step(num: u8, total: u8, icon: &str, msg: &str) {
|
||||
let tag = cyan(&format!("[{}/{}]", num, total));
|
||||
println!(" {} {} {}", tag, icon, msg);
|
||||
}
|
||||
Reference in New Issue
Block a user