package utils
import (
"fmt"
"strconv"
"strings"
"github.com/Rhymond/go-money"
)
// supported currencies
var currencies = money.Currencies{
"USD": money.GetCurrency(money.USD),
"EUR": money.GetCurrency(money.EUR),
"GBP": money.GetCurrency(money.GBP),
"JPY": money.GetCurrency(money.JPY),
"CNY": money.GetCurrency(money.CNY),
}
func ParseMoney(s string) (*money.Money, error) {
for _, c := range currencies {
numPart, ok := isCurrency(s, c)
if !ok {
continue
}
// Parse the number part
num, err := strconv.ParseUint(numPart, 10, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse number: %w", err)
}
return money.New(int64(num), c.Code), nil
}
return nil, fmt.Errorf("matching currency not found")
}
func isCurrency(s string, c *money.Currency) (string, bool) {
var numPart string
for _, tp := range c.Template {
switch tp {
case '$':
// There should be a matching grapheme in the s at this position
remaining, ok := strings.CutPrefix(s, c.Grapheme)
if !ok {
return "", false
}
s = remaining
case '1':
// There should be a number, thousands, or decimal separator in the s at this position
// Number of expected decimal places
decimalFound := -1
// Read from string until a non-number, non-thousands, non-decimal, or EOF is found
for len(s) > 0 && (string(s[0]) == c.Thousand || string(s[0]) == c.Decimal || '0' <= s[0] && s[0] <= '9') {
// If the character is a number
if '0' <= s[0] && s[0] <= '9' {
// If we've hit decimal limit, break
if decimalFound == 0 {
break
}
// add the number to the numPart
numPart += string(s[0])
// Decrement the decimal count
// If the decimal has been found, `decimalFound` is positive
// If the decimal hasn't been found, `decimalFound` is negative, and decrementing it does nothing
decimalFound--
}
// If decimal has been found (>= 0) and the character is a thousand separator or decimal separator,
// then the number is invalid
if decimalFound >= 0 && (string(s[0]) == c.Thousand || string(s[0]) == c.Decimal) {
return "", false
}
// If the character is a decimal separator, set `decimalFound` to the number of
// expected decimal places for the currency
if string(s[0]) == c.Decimal {
decimalFound = c.Fraction
}
// Move to the next character
s = s[1:]
}
if decimalFound > 0 {
// If there should be more decimal places, add them
numPart += strings.Repeat("0", decimalFound)
} else if decimalFound < 0 {
// If no decimal was found, add the expected number of decimal places
numPart += strings.Repeat("0", c.Fraction)
}
case ' ':
// There should be a space in the s at this position
if len(s) == 0 || s[0] != ' ' {
return "", false
}
s = s[1:]
default:
panic(fmt.Sprintf("unsupported template character: %c", tp))
}
}
return numPart, true
}
-test'>deno-test
Unnamed repository; edit this file 'description' to name the repository.