From a2b580eb232542e67ee3877bdc95f275aea13c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pinochet?= Date: Mon, 1 Aug 2022 22:32:01 -0400 Subject: [PATCH] add NewFromString #108 --- README.md | 3 ++- money.go | 30 ++++++++++++++++++++++++++++++ money_test.go | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d97747d..dc281ed 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,10 @@ Initialize Money by using smallest unit value (e.g 100 represents 1 pound). Use ```go pound := money.New(100, money.GBP) ``` -Or initialize Money using the direct amount. +Or initialize Money using the direct amount as float or numeric string. ```go quarterEuro := money.NewFromFloat(0.25, money.EUR) +twoFiftyDolars := money.NewFromString("2.50", money.USD) ``` Comparison - diff --git a/money.go b/money.go index e735771..cb661ff 100644 --- a/money.go +++ b/money.go @@ -6,6 +6,8 @@ import ( "errors" "fmt" "math" + "strconv" + "strings" ) // Injection points for backward compatibility. @@ -93,6 +95,34 @@ func NewFromFloat(amount float64, currency string) *Money { return New(int64(amount*currencyDecimals), currency) } +// NewFromString creates and returns new instance of Money from a string. +// Can only parse simple float-like strings, like "1.23" USD or "1,5" ARS, not "1.23 USD", "$1.23" or "1,000" USD. +func NewFromString(amount string, currencyCode string) (*Money, error) { + currency := GetCurrency(currencyCode) + fraction := currency.Fraction + + toParse := amount + var decimals int + if pointIndex := strings.Index(amount, currency.Decimal); pointIndex != -1 { + decimals = len(amount) - pointIndex - 1 + if decimals > fraction { + decimals = fraction + } + toParse = amount[:pointIndex] + amount[pointIndex+1:pointIndex+1+decimals] + } + + parsed, err := strconv.ParseInt(toParse, 10, 64) + if err != nil { + return nil, fmt.Errorf("can't parse '%s' to money", amount) + } + + for d := decimals; d < fraction; d++ { + parsed *= 10 + } + + return New(parsed, currencyCode), nil +} + // Currency returns the currency used by Money. func (m *Money) Currency() *Currency { return m.currency diff --git a/money_test.go b/money_test.go index 196f5b5..3f6251f 100644 --- a/money_test.go +++ b/money_test.go @@ -647,6 +647,38 @@ func TestNewFromFloat(t *testing.T) { } } +func TestNewFromString(t *testing.T) { + m, err := NewFromString("12.34", EUR) + + if err != nil { + t.Error(err) + } + + if m.amount != 1234 { + t.Errorf("Expected %d got %d", 1234, m.amount) + } + + if m.currency.Code != EUR { + t.Errorf("Expected currency %s got %s", EUR, m.currency.Code) + } + + m, err = NewFromString("-1.12345", EUR) + + if err != nil { + t.Error(err) + } + + if m.amount != -112 { + t.Errorf("Expected %d got %d", -112, m.amount) + } + + _, err = NewFromString("invalid_input", EUR) + + if err.Error() != "can't parse 'invalid_input' to money" { + t.Error(err) + } +} + func TestDefaultMarshal(t *testing.T) { given := New(12345, IQD) expected := `{"amount":12345,"currency":"IQD"}`