Validation

Currencies

ISO 4217 currencies: names, symbols, minor units and the countries using them, prices formatted, and amounts turned into the cents payment APIs take.

Version 1.0.0 · 6 functions · 1 crate

#money#payments#i18n#forms

Functions

What use currencies.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use currency_info, currency_of_country from currencies (Quillo 0.6).

currency_info(code: str) → anyor an error

A currency by its code ("EUR") or number ("978"): { code, numeric, name, symbol, subunit_symbol, minor_units, countries }. minor_units is how many decimals its amounts have; countries are alpha-2 codes.

currency_of_country(alpha2: str) → listor an error

The currencies of a country by its alpha-2 code, funds left out: "CH" is ["CHF"], "PA" is ["PAB", "USD"].

currency_to_minor(amount: float, code: str) → intor an error

amount in the currency's smallest unit, as payment APIs such as Stripe take it: 12.34 EUR is 1234, 1250 JPY is 1250, 1.5 KWD is 1500. Rounded half away from zero.

currency_from_minor(minor: int, code: str) → floator an error

An amount in the smallest unit back as a number: 1234 EUR cents is 12.34.

currency_format(amount: float, code: str) → stror an error

amount written with the currency's symbol and decimals, digits grouped by commas: "€1,234.50", "¥1,235" (rounded half away from zero), "-$5.00". Shops in other countries write money other ways (1.234,50 €): this is the English way.

currency_list() → list

Every current currency's code and name, [{ code, name }], by code: for a currency select. Funds and special codes (XAU gold, XDR) are left out.

Get it

$ quillo install currencies               # currencies.qwer into this folder$ quillo install currencies --example     # and its example, currencies.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/currencies.qwer$ curl -O https://quillolang.org/plugins/files/currencies.qwe

Then, in your program, all of it or only what you call:

main.qwe
use currencies.qweruse currency_info, currency_of_country from currencies      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

[dependencies]
iso_currency = { version = "0.7", features = ["iterator"] }

Example

currencies.qwe
// Prices shown in the buyer's currency, and sent to a payment API in its smallest unit.////   quillo run currencies.qwe      then open http://localhost:8080/price?amount=1234.5&currency=EURuse "currencies.qwer"route GET /price ? { amount: float, currency: str = "USD" } = {    shown: currency_format(amount, currency)?,    charge: { amount: currency_to_minor(amount, currency)?, currency: lower(currency) },    info: currency_info(currency)?,}route GET /examples = {    yen: currency_format(1234.5, "JPY")?,    dinar: currency_to_minor(1.5, "KWD")?,    back: currency_from_minor(1234, "EUR")?,    swiss: currency_of_country("CH")?,    count: len(currency_list()),}
$ quillo run currencies.qwe$ curl 'localhost:8080/price?amount=1234.5&currency=EUR'$ curl 'localhost:8080/examples'

Source

currencies.qwer
//! ISO 4217 currencies for prices and payments: names, symbols, minor units (2 for euros, 0 for yen, 3 for dinars), the countries using them, and amounts turned into the cents payment APIs take.//! version: 1.0.0//!//! [dependencies]//! iso_currency = { version = "0.7", features = ["iterator"] }use iso_currency::{Country, Currency, IntoEnumIterator};use serde_json::{json, Value};fn find(code: &str) -> Result<Currency, String> {    let c = code.trim().to_uppercase();    if let Ok(n) = c.parse::<u16>() {        return Currency::from_numeric(n).ok_or_else(|| format!("no currency {:?}", code));    }    Currency::from_code(&c).ok_or_else(|| format!("no currency {:?}", code))}fn digits(c: Currency) -> u32 {    c.exponent().unwrap_or(0) as u32}/// A currency by its code ("EUR") or number ("978"): `{ code, numeric, name, symbol, subunit_symbol, minor_units, countries }`. `minor_units` is how many decimals its amounts have; `countries` are alpha-2 codes.pub fn currency_info(code: &str) -> Result<Value, String> {    let c = find(code)?;    let s = c.symbol();    Ok(json!({        "code": c.code(), "numeric": c.numeric(), "name": c.name(), "symbol": s.symbol, "subunit_symbol": s.subunit_symbol,        "minor_units": c.exponent(), "countries": c.used_by().iter().map(|k| k.to_string()).collect::<Vec<_>>(),    }))}/// The currencies of a country by its alpha-2 code, funds left out: "CH" is ["CHF"], "PA" is ["PAB", "USD"].pub fn currency_of_country(alpha2: &str) -> Result<Vec<String>, String> {    let k: Country = alpha2.trim().to_uppercase().parse().map_err(|_| format!("no country {:?}", alpha2))?;    Ok(Currency::from_country(k).iter().filter(|c| !c.is_fund()).map(|c| c.code().to_string()).collect())}/// `amount` in the currency's smallest unit, as payment APIs such as Stripe take it: 12.34 EUR is 1234, 1250 JPY is 1250, 1.5 KWD is 1500. Rounded half away from zero.pub fn currency_to_minor(amount: f64, code: &str) -> Result<i64, String> {    let c = find(code)?;    let v = (amount * 10f64.powi(digits(c) as i32)).round();    if !v.is_finite() || v.abs() > 9e15 {        return Err("amount out of range".into());    }    Ok(v as i64)}/// An amount in the smallest unit back as a number: 1234 EUR cents is 12.34.pub fn currency_from_minor(minor: i64, code: &str) -> Result<f64, String> {    Ok(minor as f64 / 10f64.powi(digits(find(code)?) as i32))}/// `amount` written with the currency's symbol and decimals, digits grouped by commas: "€1,234.50", "¥1,235" (rounded half away from zero), "-$5.00". Shops in other countries write money other ways (1.234,50 €): this is the English way.pub fn currency_format(amount: f64, code: &str) -> Result<String, String> {    let c = find(code)?;    let d = digits(c) as usize;    let p = 10f64.powi(d as i32);    let s = format!("{:.*}", d, (amount.abs() * p).round() / p);    let (whole, frac) = s.split_once('.').map_or((s.as_str(), ""), |(w, f)| (w, f));    let mut g = String::new();    for (i, ch) in whole.chars().enumerate() {        if i > 0 && (whole.len() - i) % 3 == 0 {            g.push(',');        }        g.push(ch);    }    let sym = c.symbol().symbol;    let sym = if sym.is_empty() { format!("{} ", c.code()) } else { sym };    Ok(format!("{}{}{}{}{}", if amount < 0.0 { "-" } else { "" }, sym, g, if d > 0 { "." } else { "" }, frac))}/// Every current currency's code and name, `[{ code, name }]`, by code: for a currency select. Funds and special codes (XAU gold, XDR) are left out.pub fn currency_list() -> Vec<Value> {    let mut v: Vec<Currency> = Currency::iter().filter(|c| !c.is_fund() && !c.is_special() && c.is_superseded().is_none()).collect();    v.sort_by_key(|c| c.code());    v.iter().map(|c| json!({ "code": c.code(), "name": c.name() })).collect()}
Thread
67Useful? Like it, or add to its thread below.

Thread 0 posts

On Currencies: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.