intent_engine/
windows_console.rs

1//! Windows Console UTF-8 Support
2//!
3//! This module provides utilities for handling console encoding on Windows.
4//! It automatically configures the console to use UTF-8 encoding for proper
5//! display of Chinese and other non-ASCII characters.
6
7#[cfg(windows)]
8use windows::Win32::System::Console::{
9    GetConsoleMode, GetStdHandle, SetConsoleMode, SetConsoleOutputCP, CONSOLE_MODE,
10    ENABLE_VIRTUAL_TERMINAL_PROCESSING, STD_OUTPUT_HANDLE,
11};
12
13/// Setup Windows console for UTF-8 output
14///
15/// This function:
16/// 1. Sets the console output code page to UTF-8 (65001)
17/// 2. Enables virtual terminal processing for ANSI escape sequences
18///
19/// # Returns
20///
21/// Returns `Ok(())` if successful, or an error message if it fails.
22///
23/// # Platform-specific
24///
25/// This function only affects Windows systems. On other platforms, it's a no-op.
26///
27/// # Example
28///
29/// ```no_run
30/// # use intent_engine::windows_console::setup_windows_console;
31/// if let Err(e) = setup_windows_console() {
32///     eprintln!("Warning: Failed to setup UTF-8 console: {}", e);
33/// }
34/// ```
35#[cfg(windows)]
36pub fn setup_windows_console() -> Result<(), String> {
37    unsafe {
38        // Set console output code page to UTF-8 (65001)
39        // This ensures that our UTF-8 output is correctly interpreted
40        SetConsoleOutputCP(65001)
41            .map_err(|e| format!("Failed to set console output code page to UTF-8: {}", e))?;
42
43        // Get the standard output handle
44        let handle = match GetStdHandle(STD_OUTPUT_HANDLE) {
45            Ok(h) => h,
46            Err(e) => return Err(format!("Failed to get stdout handle: {}", e)),
47        };
48
49        // Enable virtual terminal processing
50        // This allows ANSI escape sequences to work properly
51        let mut mode = CONSOLE_MODE(0);
52        if GetConsoleMode(handle, &mut mode).is_ok() {
53            let new_mode = mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
54            if SetConsoleMode(handle, new_mode).is_err() {
55                // This is non-critical, so we just log a warning
56                eprintln!(
57                    "Warning: Could not enable virtual terminal processing. ANSI colors may not work."
58                );
59            }
60        }
61    }
62
63    Ok(())
64}
65
66/// Setup Windows console for UTF-8 output (no-op on non-Windows platforms)
67#[cfg(not(windows))]
68pub fn setup_windows_console() -> Result<(), String> {
69    Ok(())
70}
71
72/// Check if the current console is using UTF-8 encoding
73///
74/// # Returns
75///
76/// Returns `true` if the console is using UTF-8 (code page 65001),
77/// `false` otherwise.
78///
79/// # Platform-specific
80///
81/// On non-Windows platforms, this always returns `true`.
82#[cfg(windows)]
83pub fn is_console_utf8() -> bool {
84    use windows::Win32::System::Console::GetConsoleOutputCP;
85
86    unsafe {
87        let cp = GetConsoleOutputCP();
88        cp == 65001 // UTF-8 code page
89    }
90}
91
92#[cfg(not(windows))]
93pub fn is_console_utf8() -> bool {
94    true
95}
96
97/// Detect the current console code page
98///
99/// # Returns
100///
101/// Returns the current code page number (e.g., 936 for GBK, 65001 for UTF-8)
102///
103/// # Platform-specific
104///
105/// On non-Windows platforms, this returns 65001 (UTF-8).
106#[cfg(windows)]
107pub fn get_console_code_page() -> u32 {
108    use windows::Win32::System::Console::GetConsoleOutputCP;
109
110    unsafe { GetConsoleOutputCP() }
111}
112
113#[cfg(not(windows))]
114pub fn get_console_code_page() -> u32 {
115    65001 // UTF-8
116}
117
118/// Get a user-friendly name for a Windows code page
119///
120/// # Arguments
121///
122/// * `code_page` - The code page number
123///
124/// # Returns
125///
126/// A string describing the code page (e.g., "UTF-8", "GBK", "Unknown")
127pub fn code_page_name(code_page: u32) -> &'static str {
128    match code_page {
129        65001 => "UTF-8",
130        936 => "GBK (Simplified Chinese)",
131        950 => "Big5 (Traditional Chinese)",
132        932 => "Shift-JIS (Japanese)",
133        949 => "EUC-KR (Korean)",
134        437 => "OEM United States",
135        1252 => "Western European (Windows)",
136        _ => "Unknown",
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn test_setup_console() {
146        // Should not panic
147        let result = setup_windows_console();
148        assert!(result.is_ok());
149    }
150
151    #[test]
152    fn test_is_console_utf8() {
153        // After setup, console should be UTF-8 (on Windows)
154        let _ = setup_windows_console();
155
156        #[cfg(windows)]
157        {
158            let is_utf8 = is_console_utf8();
159            // This might fail in some CI environments, so we just check it doesn't panic
160            let _ = is_utf8;
161        }
162
163        #[cfg(not(windows))]
164        {
165            assert!(is_console_utf8());
166        }
167    }
168
169    #[test]
170    fn test_code_page_names() {
171        assert_eq!(code_page_name(65001), "UTF-8");
172        assert_eq!(code_page_name(936), "GBK (Simplified Chinese)");
173        assert_eq!(code_page_name(950), "Big5 (Traditional Chinese)");
174        assert_eq!(code_page_name(12345), "Unknown");
175    }
176
177    #[test]
178    fn test_get_code_page() {
179        let cp = get_console_code_page();
180
181        #[cfg(windows)]
182        {
183            // After setup, should be UTF-8
184            let _ = setup_windows_console();
185            let cp_after = get_console_code_page();
186            assert_eq!(cp_after, 65001);
187        }
188
189        #[cfg(not(windows))]
190        {
191            assert_eq!(cp, 65001);
192        }
193    }
194}