use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
use amxml::dom::NodePtr;
use chrono::Utc;
use phf_codegen::Map;
use super::Error;
const CATEGORIES_TYPE_SIG: &str = "pub static CATEGORIES: ::phf::Map<&'static str, Category> = ";
pub struct CategoryMap(Map<String>);
impl CategoryMap {
pub fn generate(root: &NodePtr) -> Result<Self, Error> {
let mut map = Map::new();
parse_main_categories(&root, &mut map)?;
parse_additional_categories(&root, &mut map)?;
parse_reserved_categories(&root, &mut map)?;
Ok(CategoryMap(map))
}
pub fn write_file(&self, out: &Path) -> Result<(), Error> {
let CategoryMap(ref map) = *self;
let mut file = BufWriter::new(File::create(&out)?);
writeln!(
file,
"// Generated by the build script at {}. Do not edit directly!\n",
Utc::now()
)?;
writeln!(file, "/// Static hash map of desktop menu categories.")?;
write!(file, "{}", CATEGORIES_TYPE_SIG)?;
map.build(&mut file)?;
write!(file, ";\n")?;
Ok(())
}
}
fn parse_main_categories(root: &NodePtr, map: &mut Map<String>) -> Result<(), Error> {
let entries = root.get_nodeset(
"//sect1[@id='main-category-registry']/para/informaltable/tgroup/tbody/row/entry",
)?;
let names = entries
.chunks(3)
.flat_map(|row| row.into_iter().flat_map(|entry| entry.nth_child(0)).nth(0))
.map(|name| name.value());
for name in names {
let requires = match name.as_str() {
"Audio" | "Video" => "\"AudioVideo\"",
_ => "",
};
let init_str = format!("Category::Main {{ requires: &[{}] }}", requires);
map.entry(name, &init_str);
}
Ok(())
}
fn parse_additional_categories(root: &NodePtr, map: &mut Map<String>) -> Result<(), Error> {
let entries = root.get_nodeset(
"//sect1[@id='additional-category-registry']/para/informaltable/tgroup/tbody/row/entry",
)?;
let rows = entries.chunks(3).map(|row| {
row.into_iter()
.flat_map(|entry| entry.nth_child(0).map(|text| text.value()))
});
for mut row in rows {
let name = row.next().expect("Couldn't get name column from table row");
let related_err: String = row.skip(1).collect();
let related = related_err.replace("QT", "Qt");
let suggests = if related.contains(" or ") {
related.split(" or ").collect()
} else if !related.is_empty() {
vec![related.as_str()]
} else {
Vec::new()
};
let init_str = format!("Category::Additional {{ suggests: &{:?} }}", suggests);
map.entry(name, &init_str);
}
Ok(())
}
fn parse_reserved_categories(root: &NodePtr, map: &mut Map<String>) -> Result<(), Error> {
let entries = root.get_nodeset(
"//sect1[@id='reserved-category-registry']/para/informaltable/tgroup/tbody/row/entry",
)?;
let names = entries
.chunks(2)
.flat_map(|row| row.into_iter().flat_map(|entry| entry.nth_child(0)).nth(0))
.map(|name| name.value());
for name in names {
map.entry(name, "Category::Reserved");
}
Ok(())
}