use proc_macro::TokenStream;
use quote::{format_ident, quote};
use xml::{EventReader, reader::XmlEvent};
fn get_country_list() -> Vec<String> {
let file = include_str!(concat!(env!("OUT_DIR"), "/ShortNumberMetadata.xml"));
let mut countries = Vec::new();
let parser = EventReader::from_str(file);
for e in parser {
if let Ok(XmlEvent::StartElement {
name, attributes, ..
}) = e
&& name.borrow().local_name == "territory"
&& let Some(id_attr) = attributes.iter().find(|attr| attr.name.local_name == "id")
{
countries.push(id_attr.value.clone());
}
}
countries
}
#[proc_macro]
pub fn countries_enum(name: TokenStream) -> TokenStream {
let country_list = get_country_list();
let countries = country_list
.iter()
.map(|country| format_ident!("{}", country.to_uppercase()));
let str_mapper = country_list.iter().map(|country| {
let item = format_ident!("{}", country.to_uppercase());
let value = proc_macro2::Literal::string(country);
quote! { Self::#item => #value }
});
let from_str_mapper = country_list.iter().map(|country| {
let item = format_ident!("{}", country.to_uppercase());
let value = proc_macro2::Literal::string(country);
quote! { #value => ::core::result::Result::Ok(Self::#item) }
});
let name = format_ident!("{}", name.to_string());
quote! {
#[derive(
::core::fmt::Debug,
::core::clone::Clone,
::core::marker::Copy,
::core::cmp::PartialEq,
::core::cmp::Eq,
::core::hash::Hash,
::core::cmp::PartialOrd,
::core::cmp::Ord
)]
pub enum #name {
#(#countries),*
}
impl ::core::convert::AsRef<str> for #name {
fn as_ref(&self) -> &str {
match self {
#(#str_mapper),*
}
}
}
impl ::core::fmt::Display for #name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_str(self.as_ref())
}
}
impl ::core::str::FromStr for #name {
type Err = String;
fn from_str(s: &str) -> ::core::result::Result<Self, Self::Err> {
match s {
#(#from_str_mapper,)*
_ => ::core::result::Result::Err(format!("Unknown country code: {}", s)),
}
}
}
}
.into()
}