1 use anyhow::{anyhow, Result as AnyResult};
3 collections::{BTreeMap, HashSet, VecDeque},
5 fs::{read_to_string, File},
6 io::{Error as IoError, Write},
10 #[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
18 type CodepageNumber = usize;
20 fn process_converter<'a>(
21 fields: &Vec<&'a str>,
22 codepages: &mut BTreeMap<CodepageNumber, BTreeMap<Source, Vec<&'a str>>>,
24 if fields.is_empty() || fields[0] == "{" {
28 let mut cps: BTreeMap<Source, CodepageNumber> = BTreeMap::new();
29 let mut iana = VecDeque::new();
30 let mut other = VecDeque::new();
32 let mut iter = fields.iter().peekable();
33 while let Some(&name) = iter.next() {
34 if iter.next_if(|&&s| s == "{").is_some() {
35 let mut standards = HashSet::new();
37 let &standard = iter.next().expect("missing `}` in list of standards");
41 standards.insert(standard);
44 if standards.contains("IANA*") {
45 iana.push_front(name);
46 } else if standards.contains("IANA") {
48 } else if standards.iter().any(|&s| s.ends_with('*')) {
49 other.push_front(name);
51 other.push_back(name);
54 // Untagged names are completely nonstandard.
58 if let Some(number) = name.strip_prefix("cp") {
59 if let Ok(number) = number.parse::<CodepageNumber>() {
60 cps.insert(Source::Codepage, number);
64 if let Some(number) = name.strip_prefix("windows-") {
65 if let Ok(number) = number.parse::<CodepageNumber>() {
66 cps.insert(Source::Windows, number);
70 if let Some(number) = name.strip_prefix("ibm-") {
71 if let Ok(number) = number.parse::<CodepageNumber>() {
72 cps.insert(Source::Ibm, number);
77 // If there are no tagged names then this is completely nonstandard.
78 if iana.is_empty() && other.is_empty() {
82 let all: Vec<&str> = iana.into_iter().chain(other).collect();
83 for (source, number) in cps {
87 .insert(source, all.clone());
92 codepages: &BTreeMap<CodepageNumber, BTreeMap<Source, Vec<&str>>>,
94 ) -> Result<(), IoError> {
95 let mut file = File::create(file_name)?;
99 use lazy_static::lazy_static;
100 use std::collections::HashMap;
103 static ref CODEPAGE_NUMBER_TO_NAME: HashMap<i32, &'static str> = {
104 let mut map = HashMap::new();
109 for (&cpnumber, value) in codepages.iter() {
110 let source = value.keys().max().unwrap();
111 let name = value[source][0];
112 writeln!(file, " map.insert({cpnumber}, \"{name}\");")?;
118 static ref CODEPAGE_NAME_TO_NUMBER: HashMap<&'static str, u32> = {
119 let mut map = HashMap::new();
124 let mut names: BTreeMap<String, BTreeMap<Source, Vec<CodepageNumber>>> = BTreeMap::new();
125 for (&cpnumber, value) in codepages.iter() {
126 for (&source, value2) in value.iter() {
127 for name in value2.iter().map(|name| name.to_ascii_lowercase()) {
138 for (name, value) in names.iter() {
139 for (_source, numbers) in value.iter().rev().take(1) {
140 writeln!(file, " map.insert(\"{name}\", {});", numbers[0])?;
154 fn main() -> AnyResult<()> {
155 println!("cargo:rerun-if-changed=build.rs");
157 let input_file = Path::new(env!("CARGO_MANIFEST_DIR")).join("convrtrs.txt");
158 println!("cargo:rerun-if-changed={}", input_file.to_string_lossy());
159 let input = read_to_string(&input_file)
160 .map_err(|e| anyhow!("{}: read failed ({e})", input_file.to_string_lossy()))?;
162 let mut codepages: BTreeMap<CodepageNumber, BTreeMap<Source, Vec<&str>>> = BTreeMap::new();
163 let mut converter: Vec<&str> = Vec::new();
164 for line in input.lines() {
167 .map(|position| &line[..position])
170 if !line.starts_with([' ', '\t']) {
171 process_converter(&converter, &mut codepages);
174 converter.extend(line.split_whitespace());
176 process_converter(&converter, &mut codepages);
178 let output_file_name = Path::new(&var_os("OUT_DIR").unwrap()).join("encodings.rs");
180 write_output(&codepages, &output_file_name)
181 .map_err(|e| anyhow!("{}: write failed ({e})", output_file_name.to_string_lossy()))?;