use std::collections::{BTreeMap, BTreeSet};
use std::path::Path;
use ucd_parse::{
self, CoreProperty, EmojiProperty, Property, UcdFileByCodepoint,
UnicodeData, UnicodeDataExpander,
};
use crate::args::ArgMatches;
use crate::error::Result;
use crate::util::{PropertyNames, PropertyValues};
pub fn command(args: ArgMatches<'_>) -> Result<()> {
let dir = args.ucd_dir()?;
let by_name = parse_properties(&dir)?;
let properties = PropertyNames::from_ucd_dir(&dir)?;
let filter = args.filter(|name| properties.canonical(name))?;
if args.is_present("list-properties") {
for name in by_name.keys() {
println!("{}", name);
}
return Ok(());
}
let mut wtr = args.writer("prop_list")?;
wtr.names(by_name.keys().filter(|n| filter.contains(n)))?;
for (name, set) in by_name {
if filter.contains(&name) {
wtr.ranges(&name, &set)?;
}
}
Ok(())
}
pub fn command_perl_word(args: ArgMatches<'_>) -> Result<()> {
let dir = args.ucd_dir()?;
let props = parse_properties(&dir)?;
let gencats = parse_general_categories(&dir)?;
let mut perlword = BTreeSet::new();
perlword.extend(&props["Alphabetic"]);
perlword.extend(&props["Join_Control"]);
perlword.extend(&gencats["Decimal_Number"]);
perlword.extend(&gencats["Nonspacing_Mark"]);
perlword.extend(&gencats["Enclosing_Mark"]);
perlword.extend(&gencats["Spacing_Mark"]);
perlword.extend(&gencats["Connector_Punctuation"]);
let mut wtr = args.writer("perl_word")?;
wtr.ranges(args.name(), &perlword)?;
Ok(())
}
fn parse_properties<P: AsRef<Path>>(
ucd_dir: P,
) -> Result<BTreeMap<String, BTreeSet<u32>>> {
let mut by_name: BTreeMap<String, BTreeSet<u32>> = BTreeMap::new();
let prop_list: Vec<Property> = ucd_parse::parse(&ucd_dir)?;
for x in &prop_list {
by_name
.entry(x.property.clone())
.or_insert(BTreeSet::new())
.extend(x.codepoints.into_iter().map(|c| c.value()));
}
let core_prop: Vec<CoreProperty> = ucd_parse::parse(&ucd_dir)?;
for x in &core_prop {
by_name
.entry(x.property.clone())
.or_insert(BTreeSet::new())
.extend(x.codepoints.into_iter().map(|c| c.value()));
}
let unicode_data: Vec<UnicodeData> = ucd_parse::parse(&ucd_dir)?;
let bidi_mirrored =
unicode_data.iter().fold(BTreeSet::new(), |mut set, x| {
if x.bidi_mirrored {
set.extend(x.codepoints().into_iter().map(|c| c.value()))
}
set
});
by_name.insert("Bidi_Mirrored".to_string(), bidi_mirrored);
let emoji_prop: Vec<EmojiProperty> = match ucd_parse::parse(&ucd_dir) {
Ok(props) => props,
Err(err) => match *err.kind() {
ucd_parse::ErrorKind::Io(_) => {
eprintln!(
"{}. skipping emoji properties. \
emoji-data.txt is included in UCD 13.0.0 and newer, and \
can be downloaded from https://unicode.org/Public/emoji/ \
for older releases.",
err,
);
vec![]
}
_ => return Err(From::from(err)),
},
};
for x in &emoji_prop {
by_name
.entry(x.property.clone())
.or_insert(BTreeSet::new())
.extend(x.codepoints.into_iter().map(|c| c.value()));
}
Ok(by_name)
}
fn parse_general_categories<P: AsRef<Path>>(
ucd_dir: P,
) -> Result<BTreeMap<String, BTreeSet<u32>>> {
let propvals = PropertyValues::from_ucd_dir(&ucd_dir)?;
let unexpanded = ucd_parse::parse(&ucd_dir)?;
let rows: Vec<_> = UnicodeDataExpander::new(unexpanded).collect();
let mut bycat: BTreeMap<String, BTreeSet<u32>> = BTreeMap::new();
for row in rows {
let gc = propvals.canonical("gc", &row.general_category)?.to_string();
bycat
.entry(gc)
.or_insert(BTreeSet::new())
.insert(row.codepoint.value());
}
Ok(bycat)
}