pub trait OemCpConverter: core::fmt::Debug {
fn encode(&self, ch: char) -> Option<u8>;
fn decode(&self, byte: u8) -> char;
}
#[derive(Debug, Default, Clone, Copy)]
pub struct LossyAsciiOemCpConverter;
impl OemCpConverter for LossyAsciiOemCpConverter {
fn encode(&self, ch: char) -> Option<u8> {
if ch.is_ascii() && !ch.is_ascii_control() {
Some(ch as u8)
} else {
None
}
}
fn decode(&self, byte: u8) -> char {
if byte < 0x80 {
byte as char
} else {
char::REPLACEMENT_CHARACTER
}
}
}
pub static DEFAULT_OEM_CONVERTER: LossyAsciiOemCpConverter = LossyAsciiOemCpConverter;
#[derive(Debug, Default, Clone, Copy)]
pub struct Cp437OemCpConverter;
impl OemCpConverter for Cp437OemCpConverter {
fn encode(&self, ch: char) -> Option<u8> {
if ch.is_ascii() && !ch.is_ascii_control() {
return Some(ch as u8);
}
CP437_HIGH
.iter()
.position(|&c| c == ch)
.map(|i| 0x80u8 + i as u8)
}
fn decode(&self, byte: u8) -> char {
if byte < 0x80 {
byte as char
} else {
CP437_HIGH[(byte - 0x80) as usize]
}
}
}
const CP437_HIGH: [char; 128] = [
'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è', 'ï', 'î', 'ì', 'Ä', 'Å',
'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', '¢', '£', '¥', '₧', 'ƒ',
'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '⌐', '¬', '½', '¼', '¡', '«', '»',
'░', '▒', '▓', '│', '┤', '╡', '╢', '╖', '╕', '╣', '║', '╗', '╝', '╜', '╛', '┐',
'└', '┴', '┬', '├', '─', '┼', '╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧',
'╨', '╤', '╥', '╙', '╘', '╒', '╓', '╫', '╪', '┘', '┌', '█', '▄', '▌', '▐', '▀',
'α', 'ß', 'Γ', 'π', 'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ', 'ε', '∩',
'≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', '\u{00A0}',
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ascii_round_trips_through_lossy() {
let c = LossyAsciiOemCpConverter;
assert_eq!(c.encode('A'), Some(b'A'));
assert_eq!(c.decode(b'A'), 'A');
}
#[test]
fn lossy_drops_non_ascii() {
let c = LossyAsciiOemCpConverter;
assert_eq!(c.encode('é'), None);
assert_eq!(c.decode(0x82), char::REPLACEMENT_CHARACTER);
}
#[test]
fn cp437_round_trips_latin_supplement() {
let c = Cp437OemCpConverter;
for ch in ['ü', 'é', 'ä', 'Ñ', 'ß', '½', 'π'] {
let byte = c
.encode(ch)
.unwrap_or_else(|| panic!("CP437 should encode {ch:?}"));
assert_eq!(c.decode(byte), ch);
}
}
#[test]
fn cp437_passes_ascii_unchanged() {
let c = Cp437OemCpConverter;
assert_eq!(c.encode('A'), Some(b'A'));
assert_eq!(c.decode(b'A'), 'A');
}
#[test]
fn cp437_rejects_unmapped_codepoints() {
let c = Cp437OemCpConverter;
assert_eq!(c.encode('\u{1F600}'), None);
}
}