use std::collections::{BTreeMap, BTreeSet};
use std::path::Path;
use std::str;
use ucd_parse::{PropertyAlias, PropertyValueAlias};
use ucd_util;
use error::Result;
#[derive(Clone, Debug)]
pub struct Filter {
include: BTreeSet<String>,
exclude: BTreeSet<String>,
}
impl Filter {
pub fn new<F: FnMut(&str) -> Result<String>>(
include_list: Option<String>,
exclude_list: Option<String>,
mut canonicalize: F,
) -> Result<Filter> {
let (mut include, mut exclude) = (BTreeSet::new(), BTreeSet::new());
if let Some(include_list) = include_list {
for name in include_list.split(",") {
include.insert(canonicalize(name.trim())?.to_string());
}
}
if let Some(exclude_list) = exclude_list {
for name in exclude_list.split(",") {
exclude.insert(canonicalize(name.trim())?.to_string());
}
}
Ok(Filter { include, exclude })
}
pub fn contains(&self, name: &str) -> bool {
if self.exclude.contains(name) {
false
} else {
self.include.is_empty() || self.include.contains(name)
}
}
}
#[derive(Clone, Debug)]
pub struct PropertyNames(pub BTreeMap<String, String>);
impl PropertyNames {
pub fn from_ucd_dir<P: AsRef<Path>>(ucd_dir: P) -> Result<PropertyNames> {
use ucd_parse::UcdFile;
let mut map = BTreeMap::new();
for result in PropertyAlias::from_dir(ucd_dir)? {
let a = result?;
let canon = a.long.to_string();
let make_key = |mut value| {
ucd_util::symbolic_name_normalize(&mut value);
value
};
for alias in a.aliases {
map.insert(make_key(alias), canon.clone());
}
map.insert(make_key(a.abbreviation), canon.clone());
map.insert(make_key(a.long), canon);
}
Ok(PropertyNames(map))
}
pub fn canonical(&self, key: &str) -> Result<String> {
let mut key = key.to_string();
ucd_util::symbolic_name_normalize(&mut key);
match self.0.get(&key).map(|v| &**v) {
Some(v) => Ok(v.to_string()),
None => err!("unrecognized property: {:?}", key),
}
}
}
#[derive(Clone, Debug)]
pub struct PropertyValues {
pub property: PropertyNames,
pub value: BTreeMap<String, BTreeMap<String, String>>,
}
impl PropertyValues {
pub fn from_ucd_dir<P: AsRef<Path>>(ucd_dir: P) -> Result<PropertyValues> {
use ucd_parse::UcdFile;
let props = PropertyNames::from_ucd_dir(&ucd_dir)?;
let mut outer_map = BTreeMap::new();
for result in PropertyValueAlias::from_dir(ucd_dir)? {
let a = result?;
let prop = props.canonical(&a.property)?.to_string();
let canon = a.long.to_string();
let make_key = |mut value| {
ucd_util::symbolic_name_normalize(&mut value);
value
};
let mut inner_map = outer_map
.entry(prop)
.or_insert(BTreeMap::new());
if let Some(n) = a.numeric {
inner_map.insert(make_key(n.to_string()), canon.clone());
}
for alias in a.aliases {
inner_map.insert(make_key(alias), canon.clone());
}
inner_map.insert(make_key(a.abbreviation), canon.clone());
inner_map.insert(make_key(a.long), canon);
}
let scripts = outer_map["Script"].clone();
outer_map.insert("Script_Extensions".to_string(), scripts);
Ok(PropertyValues { property: props, value: outer_map })
}
pub fn values<'a>(
&'a self,
property: &str,
) -> Result<&'a BTreeMap<String, String>> {
let property = self.property.canonical(property)?;
match self.value.get(&*property) {
Some(map) => Ok(map),
None => err!("unrecognized property name: {:?}", property),
}
}
pub fn canonical(
&self,
property: &str,
value: &str,
) -> Result<String> {
let property = self.property.canonical(property)?;
let mut value = value.to_string();
ucd_util::symbolic_name_normalize(&mut value);
match self.value.get(&*property).and_then(|m| m.get(&value)) {
Some(v) => Ok(v.to_string()),
None => err!(
"unrecognized property name/value: {:?}", (property, value)),
}
}
}
pub fn to_ranges<I: IntoIterator<Item=u32>>(it: I) -> Vec<(u32, u32)> {
let mut codepoints: Vec<u32> = it.into_iter().collect();
codepoints.sort();
codepoints.dedup();
let mut ranges = vec![];
for cp in codepoints {
range_add(&mut ranges, cp);
}
ranges
}
pub fn range_add(ranges: &mut Vec<(u32, u32)>, codepoint: u32) {
if let Some(&mut (_, ref mut end)) = ranges.last_mut() {
assert!(*end < codepoint);
if codepoint == *end + 1 {
*end = codepoint;
return;
}
}
ranges.push((codepoint, codepoint));
}
pub fn to_range_values<I>(it: I) -> Vec<(u32, u32, u64)>
where I: IntoIterator<Item=(u32, u64)>
{
let mut codepoints: Vec<(u32, u64)> = it.into_iter().collect();
codepoints.sort();
codepoints.dedup();
let mut ranges = vec![];
for (cp, value) in codepoints {
range_value_add(&mut ranges, cp, value);
}
ranges
}
pub fn range_value_add(
ranges: &mut Vec<(u32, u32, u64)>,
codepoint: u32,
value: u64,
) {
if let Some(&mut (_, ref mut end, value2)) = ranges.last_mut() {
assert!(*end < codepoint);
if codepoint == *end + 1 && value == value2 {
*end = codepoint;
return;
}
}
ranges.push((codepoint, codepoint, value));
}
pub fn print_property_values(
propvals: &PropertyValues,
property: &str,
) -> Result<()> {
let by_alias = propvals.values(property)?;
let mut by_canonical: BTreeMap<&str, Vec<&str>> = BTreeMap::new();
for (alias, canonical) in by_alias {
by_canonical.entry(&**canonical).or_insert(vec![]).push(&**alias);
}
for (canonical, mut aliases) in by_canonical {
aliases.sort();
println!("{} (aliases: {})", canonical, aliases.join(", "));
}
Ok(())
}