Enhance CLI output: UTF-8 console codepage, VT colors, clean status cards

This commit is contained in:
2026-09-07 16:01:50 +02:00
parent a50dc04bb3
commit 681fb0c032
4 changed files with 194 additions and 64 deletions
+99
View File
@@ -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);
}