1 stable release
2.0.4 | Feb 14, 2023 |
---|
#1819 in Parser implementations
30KB
507 lines
Table of Contents
About
The xand_money
submodule provides methods for safely working with monetary values in xand_banks
and other crates.
It exposes fiat types such as Usd
for currency and a Xand
type to represent digital claims. The class is
extensible to other fiat currencies.
The Xand
type represents values on the Xand network, which cannot be negative.
The fiat type Usd
represents monetary values as communicated to/from banks, which can be negative.
This crate is designed to prevent all numeric data loss (rounding and precision errors inherent working with IEE-754-encoded floating point values), which is especially important when working with money.
Getting started
This crate is available from the TPFS internal crates registry.
Add xand_money
to your project in Cargo.toml
with:
xand_money = { version = "<desired version>", registry = "tpfs" }
Money Types
Usd
Usd
's inner type is (currently) a Decimal
which can be returned in major units (1.23) or minor units (123).
Note: Usd
inner type is a private implementation detail and should not be depended upon.
Conversions
Into Usd
The Usd
type enables conversions from f64
, i64
, u64
, and &str
types we might receive from a third party bank API
into a USD monetary representation within xand-banks
and other crates.
For example, converting from a float:
let foo: f64 = 1.23;
let usd = Usd::from_f64(foo)?;
From Usd
The Usd
type enables conversions to f64
:
let usd = Usd::from_f64(15_149.99_f64)?;
let num: f64 = f64::try_from(usd)?;
The Usd
type also enables minor unit representations for u64
and i64
, though given the differing bounds of these types it is
possible for representation to fail, in which case an Error will be returned instead:
let usd = Usd::from_f64(15_149.99_f64)?;
let i_minor_units: i64 = usd.into_i64_minor_units()?;
let u_minor_units: u64 = usd.into_u64_minor_units()?;
Usd into Decimal
Usd
can be converted into a Decimal
using one of its instance methods, allowing for conversions supported by that type,
see docs.
Equality Comparisons
Types in xand_money
implement full and partial equality for like types.
To compare different monetary values, convert them into the same money type:
let usd1 = Usd::from_f64(123.00)?;
let usd2 = Usd::from_i64(123);
assert_eq!(usd1, usd2);
Arithmetic Operations
In order to make arithmetic calculations using different monetary value representations (e.g. a Usd
and a f64
),
they must first be converted to Decimal
format by using the type's as_major_units()
or as_minor_units()
instance methods.
For example:
let usd1 = Usd::from_f64(1.23)?;
let usd2 = Usd::from_f64(1.23)?;
let usd3 = Usd::from_i64(1);
assert_ne!(usd1.as_major_units(), usd2.as_major_units() + usd3.as_major_units());
Note:
Xand
parent class can only be added to itself and cannot be transformed into other types.
Error handling
Errors produced by the xand_money
class can be consumed by a more specific error enum
in the bank implementation:
foo
.bar()
.map_or_else(
|e: MoneyError| Err(BazBankAdapterError {
source: Arc::new(MoneyError::ErrorConstructingDecimal),
message: "Failed to parse transaction amount from string".to_string()
}),
|val| Ok(val))
However, in infallible operations (such as constructing request bodies), the error will not be accessed because a failure case would be a panic:
...
let amt: Usd = xfer.amount;
XferReq {
xfer_req: Some(XferReqXferReq {
...
xfer_info: Some(XferInfo {
...
cur_amt: Some(CurAmt {
amt: match amt.as_major_units().to_f32(){
Some(amt) => Some(amt),
None => panic!(), // XferReq does not allow this conversion to fail
}
})
}),
}),
Dependencies
~0.9–1.5MB
~33K SLoC