use std::env;
use std::fmt::Write as _;
use std::fs;
use std::path::Path;
fn main() {
println!("cargo:rerun-if-changed=vendor/ucum-essence.xml");
println!("cargo:rerun-if-changed=build.rs");
let xml = fs::read_to_string("vendor/ucum-essence.xml")
.expect("vendor/ucum-essence.xml must be present");
let doc = roxmltree::Document::parse(&xml).expect("ucum-essence.xml must be valid XML");
let mut prefixes = String::new();
let mut atoms = String::new();
for node in doc.descendants() {
match node.tag_name().name() {
"prefix" => {
let code = node.attribute("Code").expect("prefix Code");
let ci_code = node.attribute("CODE").expect("prefix CODE");
let name = child_text(&node, "name").unwrap_or_default();
let value = node
.children()
.find(|c| c.tag_name().name() == "value")
.and_then(|v| v.attribute("value"))
.expect("prefix value");
let factor: f64 = value.parse().expect("prefix value f64");
writeln!(
prefixes,
" PrefixDef {{ code: {code:?}, ci_code: {ci_code:?}, name: {name:?}, factor: {factor:?} }},"
)
.unwrap();
}
"base-unit" => {
let code = node.attribute("Code").expect("base Code");
let ci_code = node.attribute("CODE").expect("base CODE");
let name = child_text(&node, "name").unwrap_or_default();
let dim = node.attribute("dim").expect("base dim");
let idx = dim_index(dim);
writeln!(
atoms,
" AtomDef {{ code: {code:?}, ci_code: {ci_code:?}, name: {name:?}, is_metric: true, is_arbitrary: false, kind: AtomKind::Base({idx}) }},"
)
.unwrap();
}
"unit" => {
let code = node.attribute("Code").expect("unit Code");
let ci_code = node.attribute("CODE").expect("unit CODE");
let name = child_text(&node, "name").unwrap_or_default();
let is_metric = node.attribute("isMetric") == Some("yes");
let is_special = node.attribute("isSpecial") == Some("yes");
let is_arbitrary = node.attribute("isArbitrary") == Some("yes");
let value = node
.children()
.find(|c| c.tag_name().name() == "value")
.expect("unit value element");
let kind = if is_special {
let func = value
.children()
.find(|c| c.tag_name().name() == "function")
.expect("special unit function");
let fname = func.attribute("name").expect("function name");
let fval: f64 = func
.attribute("value")
.expect("function value")
.parse()
.expect("f");
let funit = func.attribute("Unit").expect("function Unit");
format!(
"AtomKind::Special {{ func: {fname:?}, value: {fval:?}, unit: {funit:?} }}"
)
} else {
let unit = value.attribute("Unit").expect("value Unit");
let v: f64 = value
.attribute("value")
.expect("value attr")
.parse()
.expect("value f64");
format!("AtomKind::Derived {{ value: {v:?}, unit: {unit:?} }}")
};
writeln!(
atoms,
" AtomDef {{ code: {code:?}, ci_code: {ci_code:?}, name: {name:?}, is_metric: {is_metric}, is_arbitrary: {is_arbitrary}, kind: {kind} }},"
)
.unwrap();
}
_ => {}
}
}
let out = format!(
"// @generated by build.rs from vendor/ucum-essence.xml. Do not edit.\n\
pub static PREFIXES: &[PrefixDef] = &[\n{prefixes}];\n\n\
pub static ATOMS: &[AtomDef] = &[\n{atoms}];\n"
);
let dest = Path::new(&env::var("OUT_DIR").unwrap()).join("ucum_tables.rs");
fs::write(dest, out).expect("write generated tables");
}
fn child_text<'a>(node: &roxmltree::Node<'a, 'a>, tag: &str) -> Option<String> {
node.children()
.find(|c| c.tag_name().name() == tag)
.and_then(|c| c.text())
.map(|s| s.trim().to_string())
}
fn dim_index(dim: &str) -> usize {
match dim {
"L" => 0, "T" => 1, "M" => 2, "A" => 3, "C" => 4, "Q" => 5, "F" => 6, other => panic!("unknown base dimension letter: {other}"),
}
}