use std::collections::HashMap;
use std::env;
use std::fmt::Write as _;
use std::fs;
use std::path::PathBuf;
type Group = (char, Vec<(char, String)>);
fn parse(path: &str) -> Vec<Group> {
let src = fs::read_to_string(path).unwrap_or_else(|e| panic!("{path}: {e}"));
let mut groups: HashMap<char, Vec<(char, String)>> = HashMap::new();
let mut order: Vec<char> = Vec::new();
for line in src.lines() {
let line = line.trim_end_matches('\r');
if line.is_empty() || line.starts_with('#') {
continue;
}
let mut fields = line.splitn(3, ':');
let key = single_char(fields.next().unwrap_or(""), path, line);
let value = single_char(fields.next().unwrap_or(""), path, line);
let comment = fields.next().unwrap_or("");
if !groups.contains_key(&key) {
order.push(key);
}
groups
.entry(key)
.or_default()
.push((value, comment.to_string()));
}
let mut entries: Vec<Group> = order
.into_iter()
.map(|k| {
let g = groups.remove(&k).unwrap();
(k, g)
})
.collect();
entries.sort_by_key(|e| e.0);
entries
}
fn single_char(field: &str, path: &str, line: &str) -> char {
let mut chars = field.chars();
match (chars.next(), chars.next()) {
(Some(c), None) => c,
_ => panic!("{path}: field {field:?} is not a single char in line {line:?}"),
}
}
fn revise(symbols: &[Group]) -> Vec<Group> {
let mut revised = symbols.to_vec();
let rieul = revised
.iter_mut()
.find(|g| g.0 == 'ㄹ')
.expect("mssymbol.txt: ㄹ group missing");
assert_eq!(
rieul.1.get(3).map(|e| e.0),
Some('F'),
"mssymbol.txt: expected duplicated fullwidth F at ㄹ[3]; data changed upstream?"
);
assert!(
!rieul.1.iter().any(|e| e.0 == '°'),
"mssymbol.txt: ° already present at ㄹ; data changed upstream?"
);
rieul.1[3].0 = '°';
let mieum = revised
.iter_mut()
.find(|g| g.0 == 'ㅁ')
.expect("mssymbol.txt: ㅁ group missing");
assert!(
!mieum.1.iter().any(|e| e.0 == '㉾'),
"mssymbol.txt: ㉾ already present at ㅁ; data changed upstream?"
);
mieum.1.push(('㉾', String::new()));
revised
}
fn emit_hanja(out: &mut String, entries: &[Group]) {
out.push_str("pub(crate) static HANJA: &[(char, &[(char, &str)])] = &[\n");
for (key, candidates) in entries {
write!(out, " ({key:?}, &[").unwrap();
for (value, comment) in candidates {
write!(out, "({value:?}, {comment:?}), ").unwrap();
}
out.push_str("]),\n");
}
out.push_str("];\n");
}
fn emit_symbols(out: &mut String, name: &str, entries: &[Group]) {
writeln!(out, "pub(crate) static {name}: &[(char, &[char])] = &[").unwrap();
for (key, candidates) in entries {
write!(out, " ({key:?}, &[").unwrap();
for (value, _) in candidates {
write!(out, "{value:?}, ").unwrap();
}
out.push_str("]),\n");
}
out.push_str("];\n");
}
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=data/hanja.txt");
println!("cargo:rerun-if-changed=data/mssymbol.txt");
let hanja = parse("data/hanja.txt");
let symbols = parse("data/mssymbol.txt");
let revised = revise(&symbols);
let mut out = String::new();
emit_hanja(&mut out, &hanja);
emit_symbols(&mut out, "SYMBOLS", &symbols);
emit_symbols(&mut out, "SYMBOLS_REVISED", &revised);
let dest = PathBuf::from(env::var("OUT_DIR").unwrap()).join("tables.rs");
fs::write(dest, out).unwrap();
}