pub(super) fn decode(bytes: &[u8]) -> String {
match std::str::from_utf8(bytes) {
Ok(text) => text.to_string(),
Err(_) => decode_native(bytes),
}
}
#[cfg(windows)]
fn decode_native(bytes: &[u8]) -> String {
use windows_sys::Win32::System::Console::GetConsoleOutputCP;
let cp = match unsafe { GetConsoleOutputCP() } {
0 | 65001 => 1252,
n => n,
};
let Some(encoding) = codepage_encoding(cp) else {
return String::from_utf8_lossy(bytes).into_owned();
};
encoding.decode(bytes).0.into_owned()
}
#[cfg(not(windows))]
fn decode_native(bytes: &[u8]) -> String {
String::from_utf8_lossy(bytes).into_owned()
}
#[cfg(windows)]
fn codepage_encoding(cp: u32) -> Option<&'static encoding_rs::Encoding> {
let label: &str = match cp {
437 | 850 | 858 | 1252 => "windows-1252",
866 => "ibm866",
1251 => "windows-1251",
1250 => "windows-1250",
1253 => "windows-1253",
1254 => "windows-1254",
1255 => "windows-1255",
1256 => "windows-1256",
1257 => "windows-1257",
1258 => "windows-1258",
932 => "shift_jis",
936 => "gbk",
949 => "euc-kr",
950 => "big5",
_ => return None,
};
encoding_rs::Encoding::for_label(label.as_bytes())
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::decode;
#[test]
fn plain_utf8_round_trips() {
assert_eq!(decode("hello".as_bytes()), "hello");
assert_eq!(decode("naïve".as_bytes()), "naïve");
}
#[cfg(windows)]
#[test]
fn non_utf8_falls_back_to_the_code_page() {
assert!(!decode(&[b'c', b'a', b'f', 0xE9]).contains('\u{fffd}'));
}
}