currencies

Crates.iocurrencies
lib.rscurrencies
version0.4.1
sourcesrc
created_at2023-08-13 07:10:20.118495
updated_at2024-06-21 06:36:36.364493
descriptionAllows for generic manipulation of currencies (both real-world and cryptocurrencies) with optionally compile-time enforced checked math and support for all ISO-4217 currencies.
homepage
repositoryhttps://github.com/sam0x17/currencies
max_upload_size
id943173
size11,623
Sam Johnson (sam0x17)

documentation

README

💰 currencies

Crates.io docs.rs Build Status MIT License

This crate provides a generic Currency and corresponding Amount type that can handle basic arithmetic operations and formatting of arbitrary currencies and cryptocurrencies. Main features include:

Features

  • Built-in support for all ISO-4217 currencies with proper precision and formatting
  • Support for a variety of cryptocurrencies, also with proper underlying data types and formatting. Accurate implementations for ETH, BTC, DOT, and a variety of other cryptocurrencies are included.
  • The ability to specify whether an Amount is forced to only make use of unchecked math, or not, at compile-time. Normally this is impossible to control since the core:ops operators are set up such that the checked operators require their unchecked counterparts to be implemented on the host type, however I have gone out of my way to make it possible to implement unchecked math only, and control it easily with a Amount<ETH, Checked>-style switch. This is extremely desirable for scenarios where panicking could cause a catastrophic issue, and the way it is set up, programmers are forced to consume the Option returned by the checked ops.
  • An easy-to-use macro, define_currency! that can define new currencies on-the-fly.
  • A painstakingly wrapped version of primitive_types::U256 that implements many more useful num-traits and num-integer traits than what Parity includes with the num-traits feature, and are often required when working with amounts of a currency.
  • All provided currencies implement most useful num-traits and num-integer traits.
  • Thorough testing of all of the above.

Examples

#[test]
fn show_off_currency_math() {
    use currency::*;

    let apple_cost = amt!(USD, "$3.24");
    let orange_cost = Amount::<USD>::from_raw(7_97);
    assert!(apple_cost < orange_cost);
    assert!(apple_cost + orange_cost > orange_cost);
    assert_eq!(format!("{}", apple_cost * orange_cost), "$25.82");
    assert_eq!(format!("{}", apple_cost * 3), "$9.72");

    let mut total = amt!(DOT, "57622449841.0000000004 DOT");
    total -= amt!(DOT, "1000.0 DOT");
    total *= Amount::from_raw(2_0000000000u64.into());
    assert_eq!(format!("{}", total), "115244897682.0000000008 DOT");
}

#[test]
fn show_off_checked_math() {
    use currency::*;
    use safety::*;

    // When using currency amounts with `Safety = Checked`, the Amount struct has been specially set
    // up so that only checked math will be allowed, and you can still use the normal
    // operator-based syntax. Thus currency amounts like this should never panic and are
    // suitable for use in critical/infallible environments.
    let drink_cost = amt_checked!(USD, "$6.29");
    let movie_cost = Amount::<USD, Checked>::from_raw(24_99);
    let Some(outing_cost) = drink_cost + movie_cost else {
      unimplemented!("compiler forces you to handle this!")
    };
    assert_eq!(format!("{}", outing_cost), "$31.28");
}

Future Work

  • Additional macros for defining an Amount via a decimal literal
  • Currency conversion facilities, possibly including an online data source
  • Add Signedness support to Amount
  • Additional testing
  • Support for negative amounts via an additional const generic defaulting to Positive
Commit count: 99

cargo fmt