use iso_currency::IntoEnumIterator;
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::sync::LazyLock;
const DEFAULT_MINOR_UNIT_SYMBOL: &str = "minor";
fn main() {
generate_iso().expect("failed generating iso currencies");
}
fn generate_iso() -> Result<(), String> {
let out_dir = env::var("OUT_DIR").unwrap();
let filename = "iso_currencies.rs";
let dest_path = Path::new(&out_dir).join(filename);
let mut f = File::create(&dest_path).unwrap();
writeln!(f, "use crate::Currency;").map_err(|err| err.to_string())?;
for currency in iso_currency::Currency::iter() {
let isocurrency: IsoCurrency = currency.into();
writeln!(
f,
"
/// {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct {};
impl Currency for {} {{
const CODE: &'static str = \"{}\";
const SYMBOL: &'static str = \"{}\";
const NAME: &'static str = \"{}\";
const NUMERIC: u16 = {};
const MINOR_UNIT: u16 = {};
const MINOR_UNIT_SYMBOL: &'static str = \"{}\";
const THOUSAND_SEPARATOR: &'static str = \"{}\";
const DECIMAL_SEPARATOR: &'static str = \"{}\";
}}
",
isocurrency.name.clone(),
isocurrency.code,
isocurrency.code,
isocurrency.code,
isocurrency.symbol,
isocurrency.name,
isocurrency.numeric,
isocurrency.minor_unit,
isocurrency.minor_unit_symbol,
isocurrency.separator.thousand_separator,
isocurrency.separator.decimal_separator,
)
.map_err(|err| err.to_string())?;
}
println!("cargo:rerun-if-changed=build.rs");
Ok(())
}
const COMMA_SEPARATED_THOUSAND_CURRENCIES: &[&str] = &[
"USD", "CAD", "MXN", "GTQ", "HNL", "NIO", "CRC", "PAB", "DOP", "CNY", "JPY", "KRW", "TWD", "THB", "MYR", "SGD", "PHP", "INR", "PKR", "BDT", "HKD", "LKR", "NPR", "ZAR", "BWP", "ZMW", "KES", "TZS", "UGX", "GHS", "NGN", "AUD", "NZD", "GBP", "CHF", "ISK", "SAR", "AED", "ILS",
];
struct IsoCurrency {
pub code: String,
pub symbol: String,
pub name: String,
pub numeric: u16,
pub minor_unit: u16,
pub minor_unit_symbol: String,
pub separator: Separator,
}
impl IsoCurrency {
pub(crate) fn r#override<F>(&mut self, func: F)
where
F: FnOnce(&mut Self),
{
func(self)
}
}
impl From<iso_currency::Currency> for IsoCurrency {
fn from(currency: iso_currency::Currency) -> Self {
let code = currency.code();
let symbol = currency.symbol();
let name = currency.name();
let numeric = currency.numeric();
let minor_unit = currency.exponent().unwrap_or_default();
let minor_unit_symbol = if let Some(ref minor_symbol) = symbol.subunit_symbol {
minor_symbol
} else if minor_unit == 0 {
""
} else {
DEFAULT_MINOR_UNIT_SYMBOL
};
let separator: Separator = code.to_string().into();
let mut isocurrency = Self {
code: code.into(),
symbol: symbol.to_string(),
name: name.into(),
numeric,
minor_unit,
minor_unit_symbol: minor_unit_symbol.into(),
separator,
};
for func in OVERRIDES.iter() {
isocurrency.r#override(func);
}
isocurrency
}
}
struct Separator {
pub thousand_separator: String,
pub decimal_separator: String,
}
impl From<String> for Separator {
fn from(value: String) -> Self {
if let Some(c) = iso_currency::Currency::from_code(&value)
&& COMMA_SEPARATED_THOUSAND_CURRENCIES.contains(&c.code())
{
Separator {
thousand_separator: ",".into(),
decimal_separator: ".".into(),
}
} else {
Separator {
thousand_separator: ".".into(),
decimal_separator: ",".into(),
}
}
}
}
static OVERRIDES: LazyLock<Vec<fn(&mut IsoCurrency)>> = LazyLock::new(|| vec![]);