use std::collections::{BTreeMap, BTreeSet};
use ucd_parse::{self, CoreProperty, UnicodeData};
use crate::args::ArgMatches;
use crate::error::Result;
use crate::util::{print_property_values, PropertyValues};
const DEFAULT_CLASS_ASSIGNMENTS: &[(u32, u32, &str)] = &[
(0x0600, 0x07BF, "AL"),
(0x0860, 0x086F, "AL"),
(0x08A0, 0x08FF, "AL"),
(0xFB50, 0xFDCF, "AL"),
(0xFDF0, 0xFDFF, "AL"),
(0xFE70, 0xFEFF, "AL"),
(0x00010D00, 0x00010D3F, "AL"),
(0x00010F30, 0x00010F6F, "AL"),
(0x0001EC70, 0x0001ECBF, "AL"),
(0x0001ED00, 0x0001ED4F, "AL"),
(0x0001EE00, 0x0001EEFF, "AL"),
(0x0590, 0x05FF, "R"),
(0x07C0, 0x085F, "R"),
(0x0870, 0x089F, "R"),
(0xFB1D, 0xFB4F, "R"),
(0x00010800, 0x00010CFF, "R"),
(0x00010D40, 0x00010F2F, "R"),
(0x00010F70, 0x00010FFF, "R"),
(0x0001E800, 0x0001EC6F, "R"),
(0x0001ECC0, 0x0001ECFF, "R"),
(0x0001ED50, 0x0001EDFF, "R"),
(0x0001EF00, 0x0001EFFF, "R"),
(0x20A0, 0x20CF, "ET"),
];
pub fn command(args: ArgMatches<'_>) -> Result<()> {
let dir = args.ucd_dir()?;
let propvals = PropertyValues::from_ucd_dir(&dir)?;
let rows: Vec<UnicodeData> = ucd_parse::parse(&dir)?;
let core_prop: Vec<CoreProperty> = ucd_parse::parse(&dir)?;
let use_short_names = args.is_present("short-names");
let bidi_class_name = |short_name: &str| {
if use_short_names {
Ok(short_name.to_string())
} else {
propvals.canonical("bc", short_name)
}
};
if args.is_present("list-classes") {
return print_property_values(&propvals, "Bidi_Class");
}
let mut by_type: BTreeMap<String, BTreeSet<u32>> = BTreeMap::new();
let mut assigned = BTreeSet::new();
for row in rows {
assigned.insert(row.codepoint.value());
let bc = bidi_class_name(&row.bidi_class)?;
by_type
.entry(bc)
.or_insert(BTreeSet::new())
.insert(row.codepoint.value());
}
let mut maybe_boundary_neutral = BTreeSet::new();
for x in &core_prop {
if &x.property == "Default_Ignorable_Code_Point"
|| &x.property == "Noncharacter_Code_Point"
{
maybe_boundary_neutral
.extend(x.codepoints.into_iter().map(|c| c.value()));
}
}
let left_to_right_name = bidi_class_name("L")?;
let boundary_neutral_name = bidi_class_name("BN")?;
for cp in 0..=0x10FFFF {
if assigned.contains(&cp) {
continue;
}
if let Some(class) = lookup_unassigned(cp, DEFAULT_CLASS_ASSIGNMENTS) {
let name = bidi_class_name(class)?;
by_type.get_mut(&name).unwrap().insert(cp);
} else if maybe_boundary_neutral.contains(&cp) {
by_type.get_mut(&boundary_neutral_name).unwrap().insert(cp);
} else {
by_type.get_mut(&left_to_right_name).unwrap().insert(cp);
}
}
let mut wtr = args.writer("bidi_class")?;
if args.is_present("enum") {
wtr.ranges_to_enum(args.name(), &by_type)?;
} else if args.is_present("rust-enum") {
let variants = by_type.keys().map(String::as_str).collect::<Vec<_>>();
wtr.ranges_to_rust_enum(args.name(), &variants, &by_type)?;
} else if args.is_present("combined") {
wtr.ranges_to_combined(args.name(), &by_type)?;
} else {
wtr.names(by_type.keys())?;
for (name, set) in by_type {
wtr.ranges(&name, &set)?;
}
}
Ok(())
}
fn lookup_unassigned<'a>(
codepoint: u32,
defaults: &[(u32, u32, &'a str)],
) -> Option<&'a str> {
defaults
.iter()
.find(|&&(start, end, _)| start <= codepoint && codepoint <= end)
.map(|&(_, _, bidi_class)| bidi_class)
}