use anyhow::Result;
use std::fmt::Write;
fn output_simple_mapping(module: &mut String, struct_name: &str, nice_name: &str, mapping: [Option<char>; 256], mut identity: usize) -> Result<()> {
for &ch in mapping[identity..256].iter() {
if let Some(ch) = ch {
if ch as u32 == identity as u32 {
identity += 1;
} else {
break;
}
} else {
break;
}
}
writeln!(module, "/// Implementation of {}", nice_name)?;
writeln!(module, "#[derive(Clone, Copy)]")?;
writeln!(module, "pub struct {};", struct_name)?;
if identity == 256 {
writeln!(module, "impl<I> TranscoderImpl<EncodeString, I> for {} where I: Pullable<Item = char> {{", struct_name)?;
writeln!(module, " fn transcode(&self, input: &mut PullBuffer<I>, output: &mut PushBuffer<u8>) {{")?;
writeln!(module, " while output.fits(1) {{")?;
writeln!(module, " match *input.view(1) {{")?;
writeln!(module, " [x @ '\\u{{0000}}'..='\\u{{00FF}}'] => {{")?;
writeln!(module, " output.push([ x as u32 as u8 ]);")?;
writeln!(module, " input.advance(1);")?;
writeln!(module, " }},")?;
writeln!(module, " [_] => {{")?;
writeln!(module, " input.advance(1);")?;
writeln!(module, " }},")?;
writeln!(module, " _ => break,")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, "}}")?;
writeln!(module, "impl<I> TranscoderImpl<DecodeString, I> for {} where I: Pullable<Item = u8> {{", struct_name)?;
writeln!(module, " fn transcode(&self, input: &mut PullBuffer<I>, output: &mut PushBuffer<char>) {{")?;
writeln!(module, " while output.fits(1) {{")?;
writeln!(module, " match *input.view(1) {{")?;
writeln!(module, " [x] => {{")?;
writeln!(module, " output.push([ char::from_u32(x as u32).unwrap() ]);")?;
writeln!(module, " input.advance(1);")?;
writeln!(module, " }},")?;
writeln!(module, " _ => break,")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, "}}")?;
} else if mapping[identity..256].iter().any(|x| x.is_none()) {
writeln!(module, "impl {} {{", struct_name)?;
writeln!(module, " pub const MAPPING: [Option<char>; {}] = [", 256 - identity)?;
for chunk in mapping[identity..].chunks(8) {
write!(module, " ")?;
for &ch in chunk {
match ch {
Some(ch) => write!(module, " Some('\\u{{{:04X}}}'),", ch as u32)?,
None => write!(module, " None ,")?,
}
}
writeln!(module)?;
}
writeln!(module, " ];")?;
writeln!(module, "}}")?;
writeln!(module, "impl<I> TranscoderImpl<EncodeString, I> for {} where I: Pullable<Item = char> {{", struct_name)?;
writeln!(module, " fn transcode(&self, input: &mut PullBuffer<I>, output: &mut PushBuffer<u8>) {{")?;
writeln!(module, " while output.fits(1) {{")?;
writeln!(module, " match *input.view(1) {{")?;
writeln!(module, " [x @ '\\u{{0000}}'..='\\u{{{:04X}}}'] => {{", identity - 1)?;
writeln!(module, " output.push([ x as u32 as u8 ]);")?;
writeln!(module, " input.advance(1);")?;
writeln!(module, " }},")?;
writeln!(module, " [ch] => {{")?;
writeln!(module, " if let Some(x) = Self::MAPPING.iter().position(|&x| x == Some(ch)) {{")?;
writeln!(module, " output.push([ x as u32 as u8 + {} ]);", identity)?;
writeln!(module, " }}")?;
writeln!(module, " input.advance(1);")?;
writeln!(module, " }},")?;
writeln!(module, " _ => break,")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, "}}")?;
writeln!(module, "impl<I> TranscoderImpl<DecodeString, I> for {} where I: Pullable<Item = u8> {{", struct_name)?;
writeln!(module, " fn transcode(&self, input: &mut PullBuffer<I>, output: &mut PushBuffer<char>) {{")?;
writeln!(module, " while output.fits(1) {{")?;
writeln!(module, " match *input.view(1) {{")?;
writeln!(module, " [x @ 0..={}] => {{", identity - 1)?;
writeln!(module, " output.push([ char::from_u32(x as u32).unwrap() ]);")?;
writeln!(module, " input.advance(1);")?;
writeln!(module, " }},")?;
writeln!(module, " [x] => {{")?;
writeln!(module, " if let Some(x) = Self::MAPPING[x as usize - {}] {{", identity)?;
writeln!(module, " output.push([ x ]);")?;
writeln!(module, " }}")?;
writeln!(module, " input.advance(1);")?;
writeln!(module, " }},")?;
writeln!(module, " _ => break,")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, "}}")?;
} else {
let mapping: Vec<char> = mapping[identity..256].iter().map(|&x| match x { Some(x) => x, None => unreachable!() }).collect();
writeln!(module, "impl {} {{", struct_name)?;
writeln!(module, " pub const MAPPING: [char; {}] = [", 256 - identity)?;
for chunk in mapping.chunks(8) {
write!(module, " ")?;
for &ch in chunk {
write!(module, " '\\u{{{:04X}}}',", ch as u32)?;
}
writeln!(module)?;
}
writeln!(module, " ];")?;
writeln!(module, "}}")?;
writeln!(module, "impl<I> TranscoderImpl<EncodeString, I> for {} where I: Pullable<Item = char> {{", struct_name)?;
writeln!(module, " fn transcode(&self, input: &mut PullBuffer<I>, output: &mut PushBuffer<u8>) {{")?;
writeln!(module, " while output.fits(1) {{")?;
writeln!(module, " match *input.view(1) {{")?;
writeln!(module, " [x @ '\\u{{0000}}'..='\\u{{{:04X}}}'] => {{", identity - 1)?;
writeln!(module, " output.push([ x as u32 as u8 ]);")?;
writeln!(module, " input.advance(1);")?;
writeln!(module, " }},")?;
writeln!(module, " [ch] => {{")?;
writeln!(module, " if let Some(x) = Self::MAPPING.iter().position(|&x| x == ch) {{")?;
writeln!(module, " output.push([ x as u32 as u8 + {} ]);", identity)?;
writeln!(module, " }}")?;
writeln!(module, " input.advance(1);")?;
writeln!(module, " }},")?;
writeln!(module, " _ => break,")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, "}}")?;
writeln!(module, "impl<I> TranscoderImpl<DecodeString, I> for {} where I: Pullable<Item = u8> {{", struct_name)?;
writeln!(module, " fn transcode(&self, input: &mut PullBuffer<I>, output: &mut PushBuffer<char>) {{")?;
writeln!(module, " while output.fits(1) {{")?;
writeln!(module, " match *input.view(1) {{")?;
writeln!(module, " [x @ 0..={}] => {{", identity - 1)?;
writeln!(module, " output.push([ char::from_u32(x as u32).unwrap() ]);")?;
writeln!(module, " input.advance(1);")?;
writeln!(module, " }},")?;
writeln!(module, " [x] => {{")?;
writeln!(module, " output.push([ Self::MAPPING[x as usize - {}] ]);", identity)?;
writeln!(module, " input.advance(1);")?;
writeln!(module, " }},")?;
writeln!(module, " _ => break,")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, " }}")?;
writeln!(module, "}}")?;
}
Ok(())
}
fn build_adobe_mapping(module: &mut String, struct_name: &str, nice_name: &str, path: &str, identity: usize) -> Result<()> {
println!("building mapping: {}", path);
let mut rdr = csv::ReaderBuilder::new()
.delimiter(b'\t')
.comment(Some(b'#'))
.has_headers(false)
.from_path(path)?;
let mut mapping = [None; 256];
for result in rdr.records() {
let record = result?;
let decoded: u32 = u32::from_str_radix(&record[0], 16)?;
let encoded: u8 = u8::from_str_radix(&record[1], 16)?;
mapping[encoded as usize] = char::from_u32(decoded);
}
output_simple_mapping(module, struct_name, nice_name, mapping, identity)
}
fn build_standard_mapping(module: &mut String, struct_name: &str, nice_name: &str, path: &str, identity: usize) -> Result<()> {
println!("building mapping: {}", path);
let mut rdr = csv::ReaderBuilder::new()
.delimiter(b'\t')
.comment(Some(b'#'))
.has_headers(false)
.flexible(true)
.trim(csv::Trim::All)
.from_path(path)?;
let mut mapping = [None; 256];
for result in rdr.records() {
let record = result?;
if record[1].is_empty() {
continue;
}
let encoded: u8 = u8::from_str_radix(&record[0][2..], 16)?;
let decoded: u32 = u32::from_str_radix(match &record[1][0..2] {
"0x" => &record[1][2..],
"<L" => &record[1][7..],
"<R" => &record[1][7..],
_ => panic!("invalid column value: {}", &record[1]),
}, 16)?;
mapping[encoded as usize] = char::from_u32(decoded);
}
output_simple_mapping(module, struct_name, nice_name, mapping, identity)
}
pub fn main() -> Result<()> {
let mut adobe = String::new();
writeln!(&mut adobe, "use crate::{{ Pullable, PullBuffer, PushBuffer, TranscoderImpl }};")?;
writeln!(&mut adobe, "use super::{{ EncodeString, DecodeString }};")?;
build_adobe_mapping(&mut adobe, "Standard", "Adobe Standard", "mappings/adobe/stdenc.txt", 32)?;
build_adobe_mapping(&mut adobe, "Symbol", "Adobe Symbol", "mappings/adobe/symbol.txt", 32)?;
build_adobe_mapping(&mut adobe, "ZapfDingbats", "Adobe Zapf Dingbats", "mappings/adobe/zdingbat.txt", 32)?;
std::fs::write(format!("{}{}", std::env::var("OUT_DIR")?, "/adobe.rs"), adobe)?;
let mut macos = String::new();
writeln!(&mut macos, "use crate::{{ Pullable, PullBuffer, PushBuffer, TranscoderImpl }};")?;
writeln!(&mut macos, "use super::{{ EncodeString, DecodeString }};")?;
build_standard_mapping(&mut macos, "Arabic", "macOS Arabic", "mappings/apple/ARABIC.TXT", 32)?;
build_standard_mapping(&mut macos, "Celtic", "macOS Celtic", "mappings/apple/CELTIC.TXT", 32)?;
build_standard_mapping(&mut macos, "CentralEuropean", "macOS Central European", "mappings/apple/CENTEURO.TXT", 32)?;
build_standard_mapping(&mut macos, "Croatian", "macOS Croation", "mappings/apple/CROATIAN.TXT", 32)?;
build_standard_mapping(&mut macos, "Cyrillic", "macOS Cyrillic", "mappings/apple/CYRILLIC.TXT", 32)?;
build_standard_mapping(&mut macos, "Dingbats", "macOS Dingbats", "mappings/apple/DINGBATS.TXT", 32)?;
build_standard_mapping(&mut macos, "Farsi", "macOS Farsi", "mappings/apple/FARSI.TXT", 32)?;
build_standard_mapping(&mut macos, "Gaelic", "macOS Gaelic", "mappings/apple/GAELIC.TXT", 32)?;
build_standard_mapping(&mut macos, "Greek", "macOS Greek", "mappings/apple/GREEK.TXT", 32)?;
build_standard_mapping(&mut macos, "Iceland", "macOS Iceland", "mappings/apple/ICELAND.TXT", 32)?;
build_standard_mapping(&mut macos, "Inuit", "macOS Inuit", "mappings/apple/INUIT.TXT", 32)?;
build_standard_mapping(&mut macos, "Roman", "macOS Roman", "mappings/apple/ROMAN.TXT", 32)?;
build_standard_mapping(&mut macos, "Romanian", "macOS Romanian", "mappings/apple/ROMANIAN.TXT", 32)?;
build_standard_mapping(&mut macos, "Turkish", "macOS Turkish", "mappings/apple/TURKISH.TXT", 32)?;
std::fs::write(format!("{}{}", std::env::var("OUT_DIR")?, "/macos.rs"), macos)?;
let mut etsi = String::new();
writeln!(&mut etsi, "use crate::{{ Pullable, PullBuffer, PushBuffer, TranscoderImpl }};")?;
writeln!(&mut etsi, "use super::{{ EncodeString, DecodeString }};")?;
std::fs::write(format!("{}{}", std::env::var("OUT_DIR")?, "/etsi.rs"), etsi)?;
let mut iso8859 = String::new();
writeln!(&mut iso8859, "use crate::{{ Pullable, PullBuffer, PushBuffer, TranscoderImpl }};")?;
writeln!(&mut iso8859, "use super::{{ EncodeString, DecodeString }};")?;
build_standard_mapping(&mut iso8859, "Iso8859_1", "ISO 8859-1", "mappings/iso8859/8859-1.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_2", "ISO 8859-2", "mappings/iso8859/8859-2.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_3", "ISO 8859-3", "mappings/iso8859/8859-3.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_4", "ISO 8859-4", "mappings/iso8859/8859-4.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_5", "ISO 8859-5", "mappings/iso8859/8859-5.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_6", "ISO 8859-6", "mappings/iso8859/8859-6.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_7", "ISO 8859-7", "mappings/iso8859/8859-7.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_8", "ISO 8859-8", "mappings/iso8859/8859-8.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_9", "ISO 8859-9", "mappings/iso8859/8859-9.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_10", "ISO 8859-10", "mappings/iso8859/8859-10.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_11", "ISO 8859-11", "mappings/iso8859/8859-11.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_13", "ISO 8859-13", "mappings/iso8859/8859-13.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_14", "ISO 8859-14", "mappings/iso8859/8859-14.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_15", "ISO 8859-15", "mappings/iso8859/8859-15.TXT", 0)?;
build_standard_mapping(&mut iso8859, "Iso8859_16", "ISO 8859-16", "mappings/iso8859/8859-16.TXT", 0)?;
std::fs::write(format!("{}{}", std::env::var("OUT_DIR")?, "/iso8859.rs"), iso8859)?;
let mut dos = String::new();
writeln!(&mut dos, "use crate::{{ Pullable, PullBuffer, PushBuffer, TranscoderImpl }};")?;
writeln!(&mut dos, "use super::{{ EncodeString, DecodeString }};")?;
build_standard_mapping(&mut dos, "Cp437", "DOS Codepage 437", "mappings/microsoft/dos/CP437.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp737", "DOS Codepage 737", "mappings/microsoft/dos/CP737.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp775", "DOS Codepage 775", "mappings/microsoft/dos/CP775.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp850", "DOS Codepage 850", "mappings/microsoft/dos/CP850.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp852", "DOS Codepage 852", "mappings/microsoft/dos/CP852.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp855", "DOS Codepage 855", "mappings/microsoft/dos/CP855.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp857", "DOS Codepage 857", "mappings/microsoft/dos/CP857.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp860", "DOS Codepage 860", "mappings/microsoft/dos/CP860.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp861", "DOS Codepage 861", "mappings/microsoft/dos/CP861.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp862", "DOS Codepage 862", "mappings/microsoft/dos/CP862.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp863", "DOS Codepage 863", "mappings/microsoft/dos/CP863.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp864", "DOS Codepage 864", "mappings/microsoft/dos/CP864.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp865", "DOS Codepage 865", "mappings/microsoft/dos/CP865.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp866", "DOS Codepage 866", "mappings/microsoft/dos/CP866.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp869", "DOS Codepage 869", "mappings/microsoft/dos/CP869.TXT", 0)?;
build_standard_mapping(&mut dos, "Cp874", "DOS Codepage 874", "mappings/microsoft/dos/CP874.TXT", 0)?;
std::fs::write(format!("{}{}", std::env::var("OUT_DIR")?, "/dos.rs"), dos)?;
let mut ebcdic = String::new();
writeln!(&mut ebcdic, "use crate::{{ Pullable, PullBuffer, PushBuffer, TranscoderImpl }};")?;
writeln!(&mut ebcdic, "use super::{{ EncodeString, DecodeString }};")?;
build_standard_mapping(&mut ebcdic, "Cp037", "EBCDIC Codepage 037", "mappings/microsoft/ebcdic/CP037.TXT", 0)?;
build_standard_mapping(&mut ebcdic, "Cp500", "EBCDIC Codepage 500", "mappings/microsoft/ebcdic/CP500.TXT", 0)?;
build_standard_mapping(&mut ebcdic, "Cp875", "EBCDIC Codepage 875", "mappings/microsoft/ebcdic/CP875.TXT", 0)?;
build_standard_mapping(&mut ebcdic, "Cp1026", "EBCDIC Codepage 1026", "mappings/microsoft/ebcdic/CP1026.TXT", 0)?;
std::fs::write(format!("{}{}", std::env::var("OUT_DIR")?, "/ebcdic.rs"), ebcdic)?;
let mut windows = String::new();
writeln!(&mut windows, "use crate::{{ Pullable, PullBuffer, PushBuffer, TranscoderImpl }};")?;
writeln!(&mut windows, "use super::{{ EncodeString, DecodeString }};")?;
build_standard_mapping(&mut windows, "Cp874", "Windows Codepage 874", "mappings/microsoft/windows/CP874.TXT", 0)?;
build_standard_mapping(&mut windows, "Cp1250", "Windows Codepage 1250", "mappings/microsoft/windows/CP1250.TXT", 0)?;
build_standard_mapping(&mut windows, "Cp1251", "Windows Codepage 1251", "mappings/microsoft/windows/CP1251.TXT", 0)?;
build_standard_mapping(&mut windows, "Cp1252", "Windows Codepage 1252", "mappings/microsoft/windows/CP1252.TXT", 0)?;
build_standard_mapping(&mut windows, "Cp1253", "Windows Codepage 1253", "mappings/microsoft/windows/CP1253.TXT", 0)?;
build_standard_mapping(&mut windows, "Cp1254", "Windows Codepage 1254", "mappings/microsoft/windows/CP1254.TXT", 0)?;
build_standard_mapping(&mut windows, "Cp1255", "Windows Codepage 1255", "mappings/microsoft/windows/CP1255.TXT", 0)?;
build_standard_mapping(&mut windows, "Cp1256", "Windows Codepage 1256", "mappings/microsoft/windows/CP1256.TXT", 0)?;
build_standard_mapping(&mut windows, "Cp1267", "Windows Codepage 1267", "mappings/microsoft/windows/CP1257.TXT", 0)?;
build_standard_mapping(&mut windows, "Cp1268", "Windows Codepage 1268", "mappings/microsoft/windows/CP1258.TXT", 0)?;
std::fs::write(format!("{}{}", std::env::var("OUT_DIR")?, "/windows.rs"), windows)?;
let mut misc = String::new();
writeln!(&mut misc, "use crate::{{ Pullable, PullBuffer, PushBuffer, TranscoderImpl }};")?;
writeln!(&mut misc, "use super::{{ EncodeString, DecodeString }};")?;
build_standard_mapping(&mut misc, "AtariST", "Atari ST", "mappings/misc/ATARIST.TXT", 0)?;
build_standard_mapping(&mut misc, "Cp424", "Codepage 424", "mappings/misc/CP424.TXT", 0)?;
build_standard_mapping(&mut misc, "Cp856", "Codepage 856", "mappings/misc/CP856.TXT", 0)?;
build_standard_mapping(&mut misc, "Cp1006", "Codepage 1006", "mappings/misc/CP1006.TXT", 0)?;
build_standard_mapping(&mut misc, "Koi8R", "KOI8-R", "mappings/misc/KOI8-R.TXT", 0)?;
build_standard_mapping(&mut misc, "Koi8U", "KOI8-U", "mappings/misc/KOI8-U.TXT", 0)?;
build_standard_mapping(&mut misc, "Kz1048", "KZ1028", "mappings/misc/KZ1048.TXT", 0)?;
build_standard_mapping(&mut misc, "AsciiQuotes", "US-ASCII with 0x60/0x27 as left/right single quotation mark to Unicode", "mappings/misc/US-ASCII-QUOTES.TXT", 0)?;
std::fs::write(format!("{}{}", std::env::var("OUT_DIR")?, "/misc.rs"), misc)?;
let mut next = String::new();
writeln!(&mut next, "use crate::{{ Pullable, PullBuffer, PushBuffer, TranscoderImpl }};")?;
writeln!(&mut next, "use super::{{ EncodeString, DecodeString }};")?;
build_standard_mapping(&mut next, "NextStep", "NextStep", "mappings/next/NEXTSTEP.TXT", 128)?;
std::fs::write(format!("{}{}", std::env::var("OUT_DIR")?, "/next.rs"), next)?;
Ok(())
}