Crate paft_money

Crate paft_money 

Source
Expand description

Currency and money primitives for the paft ecosystem.

Policy for ISO currencies without a minor-unit exponent (ISO-None):

  • If ISO 4217 defines a minor unit for an ISO currency, that exponent is used.
  • If ISO is silent (for example XAU, XDR), the crate consults the metadata registry by ISO code. If metadata is present, its minor_units is used.
  • If no metadata is registered, operations that require a scale return MoneyError::MetadataNotFound with the offending currency.

Registering metadata overlays: Use set_currency_metadata to register a human-friendly name and scale:

set_currency_metadata("XAU", "Gold", 3, "XAU", true, Locale::EnUs).unwrap();
set_currency_metadata("XDR", "SDR", 6, "XDR", true, Locale::EnUs).unwrap();
use paft_money::Locale;
set_currency_metadata("XAU", "Gold", 3, "XAU", true, Locale::EnUs).unwrap();
set_currency_metadata("XDR", "SDR", 6, "XDR", true, Locale::EnUs).unwrap();

Using metals/funds (recommended defaults):

  • Gold XAU: 3 or 6 decimal places are common; choose per domain needs.
  • Silver XAG: similar; often 3.
  • Platinum XPT: often 3.
  • Special Drawing Rights XDR: 6 is common. These are recommendations; the appropriate scale is domain-driven. Always register the scale you need.

§Decimal backend

The crate exposes a backend-agnostic Decimal type alongside RoundingStrategy. By default it uses rust_decimal providing 28 fractional digits of precision with a fast fixed-size representation. Alternatively, enabling the bigdecimal feature switches the backend to bigdecimal for effectively unbounded precision backed by big integers.

The public API, serde representation (amounts encoded as strings, currencies as ISO codes), and DataFrame integration remain stable across backends. The primary trade-offs are performance (the bigdecimal backend may allocate more often) and precision (see MAX_DECIMAL_PRECISION). Minor-unit scaling always uses 64-bit integers and therefore remains capped at 18 decimal places so that 10^scale fits inside an i128 when performing conversions.

§Quickstart

Create money in ISO currencies, add and subtract safely, serialize with stable representations, and convert via explicit exchange rates.

let price = Money::from_canonical_str("12.34", Currency::Iso(IsoCurrency::USD))?;
let tax   = Money::from_canonical_str("1.23",  Currency::Iso(IsoCurrency::USD))?;
let total = price.try_add(&tax)?;
assert_eq!(total.format(), "13.57 USD");

// Cross-currency addition is rejected
let eur = Money::from_canonical_str("5", Currency::Iso(IsoCurrency::EUR))?;
assert!(price.try_add(&eur).is_err());

§Currency conversion

Use an ExchangeRate to convert with explicit rounding.

let usd = Money::from_canonical_str("10.00", Currency::Iso(IsoCurrency::USD))?;
let rate = ExchangeRate::new(
    Currency::Iso(IsoCurrency::USD),
    Currency::Iso(IsoCurrency::EUR),
    Decimal::from(9) / Decimal::from(10), // 1 USD = 0.9 EUR
)?;
let eur = usd.try_convert_with(&rate, RoundingStrategy::MidpointAwayFromZero)?;
assert_eq!(eur.currency().code(), "EUR");

§Serde

Amounts serialize as strings (to avoid exponent notation); currencies serialize as their codes. Example:

let usd = Money::from_canonical_str("12.34", Currency::Iso(IsoCurrency::USD)).unwrap();
let json = serde_json::to_string(&usd).unwrap();
assert_eq!(json, "{\"amount\":\"12.34\",\"currency\":\"USD\"}");

§Currency metadata overlays

For ISO codes without a prescribed minor-unit exponent (e.g., XAU, XDR), register a scale so that rounding and minor-unit conversions are well-defined:

set_currency_metadata("XAU", "Gold", 3, "XAU", true, Locale::EnUs).unwrap();
set_currency_metadata("XDR", "SDR", 6, "XDR", true, Locale::EnUs).unwrap();
use paft_money::Locale;
set_currency_metadata("XAU", "Gold", 3, "XAU", true, Locale::EnUs).unwrap();
set_currency_metadata("XDR", "SDR", 6, "XDR", true, Locale::EnUs).unwrap();

§Feature flags

  • bigdecimal: switch to arbitrary precision decimals (slower, allocates for large values).
  • dataframe: enables serde/polars/df-derive integration for dataframes.
  • panicking-money-ops: implements Add/Sub/Mul/Div for Money that assert on invalid operations. Prefer the try_* methods for fallible APIs.
  • money-formatting: opt-in locale-aware formatting and strict parsing for Money.

When money-formatting is enabled you opt into localized rendering explicitly:

let eur = Money::from_canonical_str("1234.56", Currency::Iso(IsoCurrency::EUR)).unwrap();
assert_eq!(format!("{eur}"), "1234.56 EUR"); // canonical display stays locale-neutral
assert_eq!(eur.format_with_locale(Locale::EnEu).unwrap(), "€1.234,56");
assert_eq!(
    Money::from_str_locale("€1.234,56", Currency::Iso(IsoCurrency::EUR), Locale::EnEu)
        .unwrap()
        .format(),
    "1234.56 EUR"
);
assert_eq!(
    format!("{}", eur.localized(Locale::EnEu).with_code()),
    "€1.234,56 EUR"
);

Regardless of backend, serde and the high-level API remain stable; see MAX_DECIMAL_PRECISION and MAX_MINOR_UNIT_DECIMALS for limits that affect scaling and minor-unit conversions.

Re-exports§

pub use currency::Currency;
pub use currency_utils::MAX_DECIMAL_PRECISION;
pub use currency_utils::MAX_MINOR_UNIT_DECIMALS;
pub use currency_utils::MinorUnitError;
pub use currency_utils::clear_currency_metadata;
pub use currency_utils::currency_metadata;
pub use currency_utils::set_currency_metadata;
pub use currency_utils::try_normalize_currency_code;
pub use decimal::RoundingStrategy;
pub use error::MoneyError;
pub use error::MoneyParseError;
pub use money::ExchangeRate;
pub use money::Money;

Modules§

currency
Currency enumeration with ISO 4217 support and extensible fallback.
currency_utils
Utilities and helpers for working with Currency values.
decimal
Decimal abstraction toggled by feature flags. Feature-dependent decimal abstraction for paft-money.
error
Error types shared across the money crate.
money
Money type for representing financial values with currency.

Structs§

Decimal
Decimal represents a 128 bit representation of a fixed-precision decimal number. The finite set of values of type Decimal are of the form m / 10e, where m is an integer such that -296 < m < 296, and e is an integer between 0 and 28 inclusive.

Enums§

IsoCurrency
Re-export iso_currency::Currency for convenience.
Locale
Supported locales for money formatting/parsing.