diff --git a/.changeset/fix-windows-utf8-console-codepage.md b/.changeset/fix-windows-utf8-console-codepage.md new file mode 100644 index 00000000..768a8183 --- /dev/null +++ b/.changeset/fix-windows-utf8-console-codepage.md @@ -0,0 +1,5 @@ +--- +"@googleworkspace/cli": patch +--- + +Force UTF-8 console output on Windows by calling `SetConsoleOutputCP(65001)` at startup, preventing mojibake when the system default codepage is CP-1252 diff --git a/crates/google-workspace-cli/src/helpers/script.rs b/crates/google-workspace-cli/src/helpers/script.rs index 11bcdebe..6acd1618 100644 --- a/crates/google-workspace-cli/src/helpers/script.rs +++ b/crates/google-workspace-cli/src/helpers/script.rs @@ -169,13 +169,8 @@ fn process_file(path: &Path) -> Result, GwsError> { filename.trim_end_matches(".js").trim_end_matches(".gs"), ), "html" => ("HTML", filename.trim_end_matches(".html")), - "json" => { - if filename == "appsscript.json" { - ("JSON", "appsscript") - } else { - return Ok(None); - } - } + "json" if filename == "appsscript.json" => ("JSON", "appsscript"), + "json" => return Ok(None), _ => return Ok(None), }; diff --git a/crates/google-workspace-cli/src/main.rs b/crates/google-workspace-cli/src/main.rs index 41dcc1e1..bd143448 100644 --- a/crates/google-workspace-cli/src/main.rs +++ b/crates/google-workspace-cli/src/main.rs @@ -47,6 +47,10 @@ use error::{print_error_json, GwsError}; #[tokio::main] async fn main() { + // Force UTF-8 console output on Windows; no-op on other platforms. + #[cfg(windows)] + output::set_console_utf8(); + // Load .env file if present (silently ignored if missing) let _ = dotenvy::dotenv(); diff --git a/crates/google-workspace-cli/src/output.rs b/crates/google-workspace-cli/src/output.rs index 7c539f0d..695ab088 100644 --- a/crates/google-workspace-cli/src/output.rs +++ b/crates/google-workspace-cli/src/output.rs @@ -86,6 +86,27 @@ pub(crate) fn info(msg: &str) { eprintln!("{}", sanitize_for_terminal(msg)); } +// ── Windows console codepage ─────────────────────────────────────────── + +/// Force the Windows console output codepage to UTF-8 (CP 65001) at startup. +/// Without this, the default system codepage (typically CP-1252) mangles +/// non-ASCII characters printed by `println!` before Rust's stdio layer can +/// re-encode them. +/// +/// Failure is non-fatal — terminals that already use UTF-8 will behave +/// correctly regardless, and the call is safe to make unconditionally on any +/// Windows version that ships `kernel32.dll`. +#[cfg(windows)] +pub(crate) fn set_console_utf8() { + extern "system" { + fn SetConsoleOutputCP(wCodePageID: u32) -> i32; + } + // SAFETY: SetConsoleOutputCP is a documented Win32 API with no memory- + // safety requirements. The return value is ignored because failure is + // non-fatal — the terminal simply retains its existing codepage. + let _ = unsafe { SetConsoleOutputCP(65001) }; +} + #[cfg(test)] mod tests { use super::*; @@ -154,3 +175,22 @@ mod tests { assert!(result.contains("hello")); } } + +#[cfg(all(test, windows))] +mod windows_tests { + use super::*; + + #[test] + fn set_console_utf8_sets_codepage_65001() { + extern "system" { + fn GetConsoleOutputCP() -> u32; + } + set_console_utf8(); + // GetConsoleOutputCP returns 0 when no console is attached (e.g. in + // redirected test output). Only assert when we actually have a console. + let cp = unsafe { GetConsoleOutputCP() }; + if cp != 0 { + assert_eq!(cp, 65001, "expected UTF-8 codepage (65001), got {cp}"); + } + } +}