extern crate phf_codegen;
extern crate quick_xml;
use std::collections::btree_map::{BTreeMap, Entry};
use std::env;
use std::fs::File;
use std::io::{BufRead, Write};
use std::path::Path;
use std::str;
use quick_xml::events::Event;
use quick_xml::reader::Reader;
#[derive(Debug)]
struct Currency {
code: String,
name: String,
countries: Vec<String>,
fund: bool,
number: u16,
minor_units: Option<u8>,
}
enum Tag {
CountryName,
CurrencyName,
Code,
Number,
Units,
}
fn main() {
let mut reader = Reader::from_file("src/list_one.xml").expect("list_one.xml missing");
println!("cargo:rerun-if-changed=src/list_one.xml");
reader.trim_text(true);
let mut xml_currencies: BTreeMap<String, Currency> = BTreeMap::new();
for mut currency in Currencies::new(&mut reader) {
let entry = xml_currencies.entry(currency.code.clone());
match entry {
Entry::Occupied(mut e) => {
e.get_mut().countries.push(currency.countries.remove(0));
}
Entry::Vacant(e) => {
e.insert(currency);
}
};
}
let out_dir = env::var("OUT_DIR").unwrap();
let currencies_path = Path::new(&out_dir).join("currencies.rs");
let mut currencies = File::create(¤cies_path).expect("Can't create currencies.rs");
let phf_cur_path = Path::new(&out_dir).join("phf_cur.rs");
let mut phf_cur = File::create(&phf_cur_path).expect("Can't create phf_cur.rs");
writeln!(&mut currencies, "currency! {{ Currency;\n").unwrap();
write!(
&mut phf_cur,
"pub static CURRENCY: phf::Map<&'static str, Currency> = "
).unwrap();
let mut cur_map = phf_codegen::Map::new();
cur_map.phf_path("phf");
for (code, currency) in xml_currencies {
let cur_str = String::from("Currency::") + &code;
cur_map.entry(code.clone(), &cur_str);
writeln!(
&mut currencies,
concat!(
"{code} {{\n",
" name: {name:?},\n",
" countries: &[{countries}],\n",
" _countries_str: {countries_str:?},\n",
" fund: {fund},\n",
" number: {number},\n",
" minor_units: {minor_units:?},\n",
"}},\n"
),
code = code,
name = currency.name,
countries = &(currency.countries)
.iter()
.map(|s| format!("{:?}", s))
.collect::<Vec<_>>()
.join(", "),
countries_str = countries_str(&*currency.countries) + ".",
fund = currency.fund,
number = currency.number,
minor_units = currency.minor_units
).unwrap();
}
writeln!(&mut currencies, "}}").unwrap();
cur_map.build(&mut phf_cur).unwrap();
writeln!(&mut phf_cur, ";").unwrap();
}
struct Currencies<'a, B>
where
B: BufRead,
B: 'a,
{
reader: &'a mut Reader<B>,
buf: Vec<u8>,
}
impl<'a, B: BufRead> Currencies<'a, B> {
fn new(reader: &'a mut Reader<B>) -> Currencies<'a, B> {
Currencies {
reader: reader,
buf: Vec::new(),
}
}
}
impl<'a, B: BufRead> Iterator for Currencies<'a, B> {
type Item = Currency;
fn next(&mut self) -> Option<Currency> {
let mut tag: Tag = Tag::CountryName;
let mut country = String::new();
let mut fund = false;
let mut name = String::new();
let mut code = String::new();
let mut num: u16 = 0;
let mut units: Option<u8> = None;
loop {
match self.reader.read_event(&mut self.buf) {
Ok(Event::Start(ref e)) => match e.name() {
b"CtryNm" => tag = Tag::CountryName,
b"CcyNm" => {
tag = Tag::CurrencyName;
fund = e.attributes()
.find(|r| {
r.as_ref()
.map(|a| a.key == &b"IsFund"[..] && a.value == &b"true"[..])
.unwrap_or(false)
})
.is_some()
}
b"Ccy" => tag = Tag::Code,
b"CcyNbr" => tag = Tag::Number,
b"CcyMnrUnts" => tag = Tag::Units,
_ => (),
},
Ok(Event::Text(e)) => match tag {
Tag::CountryName => country = String::from_utf8(e.to_vec()).unwrap(),
Tag::CurrencyName => name = String::from_utf8(e.to_vec()).unwrap(),
Tag::Code => code = String::from_utf8(e.to_vec()).unwrap(),
Tag::Number => num = str::from_utf8(&e).unwrap().parse().unwrap(),
Tag::Units => match str::from_utf8(&e).unwrap() {
"N.A." => (),
s => units = Some(s.parse().expect(&format!("{} isn't u8", s))),
},
},
Ok(Event::End(e)) => match e.name() {
b"CcyNtry" => {
if code != "" {
return Some(Currency {
code: code.clone(),
name: name.clone(),
countries: vec![country.to_owned()],
fund: fund,
number: num,
minor_units: units,
});
}
}
_ => (),
},
Ok(Event::Eof) => return None,
Err(e) => panic!(
"Error at position {}: {:?}",
self.reader.buffer_position(),
e
),
_ => (),
}
self.buf.clear();
}
}
}
fn countries_str(countries: &[String]) -> String {
let len = countries.len();
if len == 1 {
countries[0].clone()
} else if len == 2 {
let mut out = String::new();
out.push_str(&countries[0]);
out.push_str(" and ");
out.push_str(&countries[1]);
out
} else if let Some((last, init)) = countries.split_last() {
let mut out = init.join(", ");
out.push_str(", and ");
out.push_str(last);
out
} else {
String::from("nowhere")
}
}