finnow/finnow-api/source/util/money.d

83 lines
2.9 KiB
D

module util.money;
import std.traits : isSomeString, EnumMembers;
/**
* Basic information about a monetary currency, as defined by ISO 4217.
* https://en.wikipedia.org/wiki/ISO_4217
*/
struct Currency {
/// The common 3-character code for the currency, like "USD".
immutable char[3] code;
/// The number of digits after the decimal place that the currency supports.
immutable ubyte fractionalDigits;
/// The ISO 4217 numeric code for the currency.
immutable ushort numericCode;
static Currency ofCode(S)(S code) if (isSomeString!S) {
if (code.length != 3) {
throw new Exception("Invalid currency code: " ~ code);
}
static foreach (c; ALL_CURRENCIES) {
if (c.code == code) return c;
}
throw new Exception("Unknown currency code: " ~ code);
}
}
/// An enumeration of all available currencies.
enum Currencies : Currency {
AUD = Currency("AUD", 2, 36),
USD = Currency("USD", 2, 840),
CAD = Currency("CAD", 2, 124),
GBP = Currency("GBP", 2, 826),
EUR = Currency("EUR", 2, 978),
CHF = Currency("CHF", 2, 756),
ZAR = Currency("ZAR", 2, 710),
JPY = Currency("JPY", 0, 392),
INR = Currency("INR", 2, 356)
}
immutable(Currency[]) ALL_CURRENCIES = cast(Currency[]) [EnumMembers!Currencies];
unittest {
assert(Currency.ofCode("USD") == Currencies.USD);
}
/**
* A monetary value consisting of an integer value, and a currency. The value
* is interpreted as a multiple of the smallest denomination of the currency,
* so for example, with USD currency, a value of 123 indicates $1.23.
*/
struct MoneyValue {
immutable Currency currency;
immutable long value;
int opCmp(in MoneyValue other) const {
if (other.currency != this.currency) return 0;
if (this.value < other.value) return -1;
if (this.value > other.value) return 1;
return 0;
}
MoneyValue opBinary(string op)(in MoneyValue rhs) const {
if (rhs.currency != this.currency)
throw new Exception("Cannot perform binary operations on MoneyValues with different currencies.");
static if (op == "+") return MoneyValue(currency, this.value + rhs.value);
static if (op == "-") return MoneyValue(currency, this.value - rhs.value);
static assert(false, "Operator " ~ op ~ " is not supported.");
}
MoneyValue opBinary(string op)(int rhs) const {
static if (op == "+") return MoneyValue(currency, this.value + rhs);
static if (op == "-") return MoneyValue(currency, this.value - rhs);
static if (op == "*") return MoneyValue(currency, this.value * rhs);
static if (op == "/") return MoneyValue(currency, this.value / rhs);
static assert(false, "Operator " ~ op ~ " is not supported.");
}
MoneyValue opUnary(string op)() const {
static if (op == "-") return MoneyValue(currency, -this.value);
static assert(false, "Operator " ~ op ~ " is not supported.");
}
}