diff options
author | 2024-08-11 13:15:50 -0700 | |
---|---|---|
committer | 2024-08-11 13:15:50 -0700 | |
commit | 6a3c21fb0b1c126849f2bbff494403bbe901448e (patch) | |
tree | 5d7805524357c2c8a9819c39d2051a4e3633a1d5 /backend/internal/utils/money.go | |
parent | 29c6040a51616e9e4cf6c70ee16391b2a3b238c9 (diff) | |
parent | f34b92ded11b07f78575ac62c260a380c468e5ea (diff) | |
download | ibd-trader-6a3c21fb0b1c126849f2bbff494403bbe901448e.tar.gz ibd-trader-6a3c21fb0b1c126849f2bbff494403bbe901448e.tar.zst ibd-trader-6a3c21fb0b1c126849f2bbff494403bbe901448e.zip |
Merge remote-tracking branch 'backend/main'
Diffstat (limited to 'backend/internal/utils/money.go')
-rw-r--r-- | backend/internal/utils/money.go | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/backend/internal/utils/money.go b/backend/internal/utils/money.go new file mode 100644 index 0000000..2dc2286 --- /dev/null +++ b/backend/internal/utils/money.go @@ -0,0 +1,99 @@ +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 +} |