varunsrin / rusty_money Goto Github PK
View Code? Open in Web Editor NEWMoney library for Rust
License: MIT License
Money library for Rust
License: MIT License
Currently, Formatter::money
does not include trailing zeros even if this results in less than params.rounding
minor digits. Is this intentional? If it is, I would appreciate an option to format a Money
instance with exactly n minor digits (instead of up to n).
If you store a rate for USD -> GBP
and then you query for the rate for GBP -> USD
, you get a None
value. This doesn't seem correct to me. Shouldn't the value returned be 1/(rate returned for USD -> GBP
)?
Money::from_str
only allows values that fit in a 32 bit integer to be parsed, while other methods allow for at least 64 bit integers.
assert_eq!(
Money::from_major(i64::MAX, iso::EUR)
.amount()
.to_i64()
.unwrap(),
i64::MAX
);
assert!(Money::from_str(&i32::MAX.to_string(), iso::EUR).is_ok());
assert!(Money::from_str(&(i32::MAX as i64 + 1).to_string(), iso::EUR).is_err()); // the problem
Similar to #74, it's currently not possible to add Money
to HashMap
/ HashSet
Would be handy to have this FromStr impl. Usually have data from a database with currency strings and need them parsed into a Currency type.
ex:
let currency: &Currency = "USD".parse().unwrap()
See PR #68
use rusty_money::{crypto, Money};
fn main() {
let text = "1.11111111111";
dbg!(Money::from_decimal(text.parse().unwrap(), crypto::ETH));
dbg!(Money::from_str(text, crypto::ETH));
}
[src/main.rs:5] Money::from_decimal(text.parse().unwrap(), crypto::ETH) = Money {
amount: 1.11111111111,
currency: Currency {
code: "ETH",
exponent: 18,
locale: EnUs,
minor_units: 1000000000000000000,
name: "Ethereum",
symbol: "ETH",
symbol_first: false,
},
}
[src/main.rs:6] Money::from_str(text, crypto::ETH) = Err(
InvalidAmount,
)
With the current API there is no way to exchange between ISO and Crypto currencies
(Taking into account that Currency
is Copy
, so we put all its fields on stack) Currency::minor_units
seems to be redundant, because it can be computed from something like dec!(10).powd(-Currency::exponent)
Write more thorough documentation and example code for:
Move from TravisCI to Github Actions
When handling monetary amounts, the precision should be that of the minor unit instead of mathematical correct fractions. Current implementation seems to maintain the mathematical fraction
let amount: Money<Currency> = Money::from_major(100, iso::EUR) / 3;
assert_eq!(amount.to_string(), "€33,33"); // that is what I expect
let triple_amount: Money<Currency> = amount * 3;
assert_eq!(triple_amount.to_string(), "€99,99"); // assert fail because expected is "€100,00", but 3 x €33,33 = €99,99
Here is a test case that will fail (and shouldn't, unless I am missing something):
#[test]
fn test_rusty_money() {
let eur = Money::from_str("2.00", iso::EUR).unwrap();
}
Euro's are divided into 100 cents (much like the GBP and the USD), and yet the parser is treating an amount like "2.00" or "2.50" as invalid. If I change that line to have 3 digits after the decimal, it magically works, even though "2.00" and "2.000" are the same number. Seems like bug, but perhaps I am missing something.
Hi, just wondering why the Currency is passed as a ref and not as owned? It makes getting started in fast manner pretty hard as pollution with lifetimes is hugely annoying in nested and complex data structures.
Greetings to all,
Need to cover other currencies, which is not covered yet. For example I tried with Egyptian Pound but it pancis.
Library should return a clear error message that this currency is not supported yet.
I am using this library to represent Money in the backend of a web-app. Sometimes I need to serialise and need to translate into another struct for that. Could we add a feature to (de-)serialise via Serde?
Alternatively perhaps we could make Iso Copy/Clone to make it easier to pass around and embed in other structs.
Current implementation requires something like this:
use {
std::collections::HashMap,
rusty_money::{Money, FormattableCurrency, iso, crypto},
};
#[derive(Debug)]
struct PriceList<'pl, T: FormattableCurrency> {
pub prices: HashMap<String, Money<'pl, T>>,
}
impl<'pl, T: FormattableCurrency> PriceList<'pl, T> {
fn new() -> Self {
Self{ prices: HashMap::new() }
}
fn set_price<S: Into<String>>(&mut self, thing: S, price: Money<'pl, T>) {
self.prices.insert(thing.into(), price);
}
fn get_price(&self, name: &str) -> Option<&Money<T>> {
self.prices.get(name)
}
}
fn main() {
let mut pl = PriceList::new();
pl.set_price("one", Money::from_major(1, iso::CNY));
pl.set_price("two", Money::from_major(1, iso::USD));
pl.set_price("coin", Money::from_str("0.01", crypto::BTC).unwrap());
// expected struct `rusty_money::iso::Currency`, found struct `rusty_money::crypto::Currency`
dbg!(&pl);
dbg!(pl.get_price("one"));
}
Adding a simple #[derive(Hash)] will make the currencies hashable, so we can use then in a hashmap
In it's current form, Exchange.get_rate consumes the exchange on every use which makes it hard to re-use an Exchange throughout a particular section of code. It would be ideal, if exchange.get_rate would borrow itself rather than consume itself when used.
An alternative fix would be to derive the Clone trait for the Exchange struct but I imagine that one would introduce more overhead on use.
use rusty_money::{iso, Money};
fn main() {
let money = Money::from_str("-0.005", iso::USD).unwrap();
assert_eq!(format!("{money}"), "$0.00");
}
Fails with:
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `"-$0.00"`,
right: `"$0.00"`', src\main.rs:5:5
The Display
impl should never print "negative zero dollars".
If the total to be allocated cannot be divided fully, it will add remainder to each ratio which in order.
For example, we need to divide $613 to participants with $98, $92, $98, $123, $102, $92. It will be:
98 => 100
92 => 94
98 => 99
123 => 124
102 => 103
92 => 93
but if we change the order of ratios to $123, $102, $98, $98, $92, $92:
123 => 125
102 => 104
98 =>99
98 =>99
92 => 93
92 => 93
This behaviour is not fair. The remainder should be allocated by ratio size, not order.
It would be a useful abstraction to allow an unknown-currency NaiveMoney
type that works as follows:
let money: NaiveMoney = NaiveMoney::from_minor(10);
let money: Money<USD> = money.with_currency(&USD);
NaiveMoney
can only be constructed and converted to a Money<C>
In the abstract, this would be for any data structure that needs to store N monetary values where the currency type is determined dynamically from some other source.
As a concrete use case, I'd like to use this to represent MONEY
from Postgres in SQLx.
Thoughts?
Money
has two fields, a rust_decimal::Decimal
instance and a reference. Both implement Copy
, so Money
should be able to derive Copy
. The Copy
documentation recommends that ‘[g]enerally speaking, if your type can implement Copy, it should.’ What do you think about making Money
implement Copy
? This would make calculations much less awkward.
Hey, I am a developer working on juspay/hyperswitch. We need support for the SLE currency, which was added in #78. We can use the crate as a git dependency, it would be great if you could release a new version of the crate when possible.
Thanks in advance!
Similar to #74, it's currently not possible to add Currency
to BTreeSet
/ BTreeMap
.
ExchangeRate uses references, if you want to have a separate function to get the rates from a third party api and create a vector of exchange rates for all of the currencies you have to return it as static. it is worse in terms of code, usability and even slower because references are usize long isntead of u8
Hi, Thanks for this great library. Amazing job.
I'm having trouble to print numbers. It'm getting different display depending on the constructor I used. Not sure it is intended...
#[test]
fn test_to_string_formatter() {
let sut = rusty_money::Money::from_minor(10000, rusty_money::iso::EUR);
let actual = format!("{}", sut);
assert_eq!("€100,00", actual)
} // pass
#[test]
fn test_to_string_formatter_from_major() {
let sut = rusty_money::Money::from_major(100, rusty_money::iso::EUR);
//let sut = Money::from_major(100);
let actual = format!("{}", sut);
assert_eq!("€100,00", actual)
} //fails with left: "€100.00". right: "€100"
Any idea how to prevent this?
In iso_currencies.rs the Romanian Leu
is defined as:
RON : {
exponent: 2,
iso_alpha_code: "RON",
iso_numeric_code: "946",
locale: EnEu,
minor_units: 1,
name: "Romanian Leu",
symbol: "ر.ق",
symbol_first: false,
},
The symbol should be L
(see the wikipedia page for the currency).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.