16e652d70Sopenharmony_ciuse std::collections::BTreeSet as Set;
26e652d70Sopenharmony_ciuse std::fs;
36e652d70Sopenharmony_ciuse std::io::{self, Write};
46e652d70Sopenharmony_ciuse std::path::Path;
56e652d70Sopenharmony_ciuse std::process;
66e652d70Sopenharmony_ci
76e652d70Sopenharmony_cipub struct Properties {
86e652d70Sopenharmony_ci    xid_start: Set<u32>,
96e652d70Sopenharmony_ci    xid_continue: Set<u32>,
106e652d70Sopenharmony_ci}
116e652d70Sopenharmony_ci
126e652d70Sopenharmony_ciimpl Properties {
136e652d70Sopenharmony_ci    pub fn is_xid_start(&self, ch: char) -> bool {
146e652d70Sopenharmony_ci        self.xid_start.contains(&(ch as u32))
156e652d70Sopenharmony_ci    }
166e652d70Sopenharmony_ci
176e652d70Sopenharmony_ci    pub fn is_xid_continue(&self, ch: char) -> bool {
186e652d70Sopenharmony_ci        self.xid_continue.contains(&(ch as u32))
196e652d70Sopenharmony_ci    }
206e652d70Sopenharmony_ci}
216e652d70Sopenharmony_ci
226e652d70Sopenharmony_cipub fn parse_xid_properties(ucd_dir: &Path) -> Properties {
236e652d70Sopenharmony_ci    let mut properties = Properties {
246e652d70Sopenharmony_ci        xid_start: Set::new(),
256e652d70Sopenharmony_ci        xid_continue: Set::new(),
266e652d70Sopenharmony_ci    };
276e652d70Sopenharmony_ci
286e652d70Sopenharmony_ci    let filename = "DerivedCoreProperties.txt";
296e652d70Sopenharmony_ci    let path = ucd_dir.join(filename);
306e652d70Sopenharmony_ci    let contents = fs::read_to_string(path).unwrap_or_else(|err| {
316e652d70Sopenharmony_ci        let suggestion =
326e652d70Sopenharmony_ci            "Download from https://www.unicode.org/Public/zipped/l5.0.0/UCD.zip and unzip.";
336e652d70Sopenharmony_ci        let _ = writeln!(io::stderr(), "{}: {err}\n{suggestion}", ucd_dir.display());
346e652d70Sopenharmony_ci        process::exit(1);
356e652d70Sopenharmony_ci    });
366e652d70Sopenharmony_ci
376e652d70Sopenharmony_ci    for (i, line) in contents.lines().enumerate() {
386e652d70Sopenharmony_ci        if line.starts_with('#') || line.trim().is_empty() {
396e652d70Sopenharmony_ci            continue;
406e652d70Sopenharmony_ci        }
416e652d70Sopenharmony_ci        let (lo, hi, name) = parse_line(line).unwrap_or_else(|| {
426e652d70Sopenharmony_ci            let _ = writeln!(io::stderr(), "{filename} line {i} is unexpected:\n{line}");
436e652d70Sopenharmony_ci            process::exit(1);
446e652d70Sopenharmony_ci        });
456e652d70Sopenharmony_ci        let set = match name {
466e652d70Sopenharmony_ci            "XID_Start" => &mut properties.xid_start,
476e652d70Sopenharmony_ci            "XID_Continue" => &mut properties.xid_continue,
486e652d70Sopenharmony_ci            _ => continue,
496e652d70Sopenharmony_ci        };
506e652d70Sopenharmony_ci        set.extend(lo..=hi);
516e652d70Sopenharmony_ci    }
526e652d70Sopenharmony_ci
536e652d70Sopenharmony_ci    properties
546e652d70Sopenharmony_ci}
556e652d70Sopenharmony_ci
566e652d70Sopenharmony_cifn parse_line(line: &str) -> Option<(u32, u32, &str)> {
576e652d70Sopenharmony_ci    let (mut codepoint, rest) = line.split_once(';')?;
586e652d70Sopenharmony_ci
596e652d70Sopenharmony_ci    let (lo, hi);
606e652d70Sopenharmony_ci    codepoint = codepoint.trim();
616e652d70Sopenharmony_ci    if let Some((a, b)) = codepoint.split_once("..") {
626e652d70Sopenharmony_ci        lo = parse_codepoint(a)?;
636e652d70Sopenharmony_ci        hi = parse_codepoint(b)?;
646e652d70Sopenharmony_ci    } else {
656e652d70Sopenharmony_ci        lo = parse_codepoint(codepoint)?;
666e652d70Sopenharmony_ci        hi = lo;
676e652d70Sopenharmony_ci    }
686e652d70Sopenharmony_ci
696e652d70Sopenharmony_ci    let name = rest.trim().split('#').next()?.trim_end();
706e652d70Sopenharmony_ci    Some((lo, hi, name))
716e652d70Sopenharmony_ci}
726e652d70Sopenharmony_ci
736e652d70Sopenharmony_cifn parse_codepoint(s: &str) -> Option<u32> {
746e652d70Sopenharmony_ci    u32::from_str_radix(s, 16).ok()
756e652d70Sopenharmony_ci}
76