aboutsummaryrefslogtreecommitdiff
path: root/backend/internal/utils/money.go
diff options
context:
space:
mode:
authorGravatar Anshul Gupta <ansg191@anshulg.com> 2024-08-11 13:15:50 -0700
committerGravatar Anshul Gupta <ansg191@anshulg.com> 2024-08-11 13:15:50 -0700
commit6a3c21fb0b1c126849f2bbff494403bbe901448e (patch)
tree5d7805524357c2c8a9819c39d2051a4e3633a1d5 /backend/internal/utils/money.go
parent29c6040a51616e9e4cf6c70ee16391b2a3b238c9 (diff)
parentf34b92ded11b07f78575ac62c260a380c468e5ea (diff)
downloadibd-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.go99
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
+}