├── .github
├── CODEOWNERS
├── dependabot.yml
└── workflows
│ ├── scorecard.yml
│ └── tests.yml
├── .gitignore
├── CITATION.cff
├── LICENSE
├── README.md
├── SECURITY.md
├── amount.go
├── amount_test.go
├── currency.go
├── currency_enum_encoding.go
├── currency_enum_encoding_test.go
├── go.mod
├── go.sum
└── testdata
├── amount-float-large.jsonl
└── amount-float-small.jsonl
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @nikolaydubina
2 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "gomod" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/.github/workflows/scorecard.yml:
--------------------------------------------------------------------------------
1 | name: Scorecard supply-chain security
2 | on:
3 | branch_protection_rule:
4 | schedule:
5 | - cron: '21 22 * * 3'
6 | push:
7 | branches: [ "master" ]
8 |
9 | permissions: read-all
10 |
11 | jobs:
12 | analysis:
13 | name: Scorecard analysis
14 | runs-on: ubuntu-latest
15 | permissions:
16 | security-events: write
17 | id-token: write
18 |
19 | steps:
20 | - name: "Checkout code"
21 | uses: actions/checkout@v3.1.0
22 | with:
23 | persist-credentials: false
24 |
25 | - name: "Run analysis"
26 | uses: ossf/scorecard-action@v2.3.1
27 | with:
28 | results_file: results.sarif
29 | results_format: sarif
30 | publish_results: true
31 |
32 | - name: "Upload artifact"
33 | uses: actions/upload-artifact@v3.1.0
34 | with:
35 | name: SARIF file
36 | path: results.sarif
37 | retention-days: 5
38 |
39 | - name: "Upload to code-scanning"
40 | uses: github/codeql-action/upload-sarif@v2.2.4
41 | with:
42 | sarif_file: results.sarif
43 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | branches: [master]
8 |
9 | permissions: read-all
10 |
11 | jobs:
12 | build:
13 | name: Build
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Check out code into the Go module directory
17 | uses: actions/checkout@v4
18 |
19 | - name: Set up Go 1.x
20 | uses: actions/setup-go@v5
21 | with:
22 | go-version: ^1.21
23 |
24 | - name: Test
25 | run: |
26 | go get -v -t -d ./...
27 | go install github.com/jstemmer/go-junit-report/v2@latest
28 | go test -coverprofile=coverage.out -covermode=atomic -cover -json -v ./... 2>&1 | go-junit-report -set-exit-code > tests.xml
29 |
30 | - name: Fuzz
31 | run: go test -list . | grep Fuzz | xargs -P 8 -I {} go test -fuzz {} -fuzztime 5s .
32 |
33 | - name: Upload test results to Codecov
34 | uses: codecov/test-results-action@v1
35 | with:
36 | token: ${{ secrets.CODECOV_TOKEN }}
37 | files: tests.xml
38 |
39 | - name: Upload coverage to Codecov
40 | uses: codecov/codecov-action@v4.1.1
41 | with:
42 | token: ${{ secrets.CODECOV_TOKEN }}
43 | files: coverage.out
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Dependency directories (remove the comment below to include it)
15 | # vendor/
16 | *.bench
--------------------------------------------------------------------------------
/CITATION.cff:
--------------------------------------------------------------------------------
1 |
2 | cff-version: 1.2.0
3 | message: If you reference this library in publication, please cite it as below.
4 | title: Fixed Point currency aware money type in Go
5 | abstract: High-performance, currency-aware fixed point money type in Go
6 | authors:
7 | - family-names: Dubina
8 | given-names: Nikolay
9 | version: 2.1
10 | date-released: 2022-06-21
11 | license: MIT
12 | repository-code: https://github.com/nikolaydubina/fpmoney
13 | url: https://github.com/nikolaydubina/fpmoney
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Nikolay Dubina
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ## 🧧 Fixed-Point Decimal Money
6 |
7 | [](https://codecov.io/gh/nikolaydubina/fpmoney)
8 | [](https://goreportcard.com/report/github.com/nikolaydubina/fpmoney)
9 | [](https://pkg.go.dev/github.com/nikolaydubina/fpmoney)
10 | [](https://github.com/avelino/awesome-go)
11 | [](https://securityscorecards.dev/viewer/?uri=github.com/nikolaydubina/fpmoney)
12 |
13 | > _**Be Precise:** using floats to represent currency is almost criminal. — Robert.C.Martin, "Clean Code" p.301_
14 |
15 | * as fast as `int64`
16 | * no `float` in parsing nor printing, does not leak precision
17 | * `ISO 4217`[^1][^2] currency
18 | * block mismatched currency arithmetics
19 | * 100 LOC
20 | * fuzz tests
21 |
22 | ```go
23 | var BuySP500Price = fpmoney.FromInt(9000, fpmoney.SGD)
24 |
25 | input := []byte(`{"sp500": {"amount": 9000.02, "currency": "SGD"}}`)
26 |
27 | type Stonks struct {
28 | SP500 fpmoney.Amount `json:"sp500"`
29 | }
30 | var v Stonks
31 | if err := json.Unmarshal(input, &v); err != nil {
32 | log.Fatal(err)
33 | }
34 |
35 | amountToBuy := fpmoney.FromInt(0, fpmoney.SGD)
36 | if v.SP500.GreaterThan(BuySP500Price) {
37 | amountToBuy = amountToBuy.Add(v.SP500.Mul(2))
38 | }
39 |
40 | fmt.Println(amountToBuy)
41 | // Output: 18000.04 SGD
42 | ```
43 |
44 | ### Ultra Small Fractions
45 |
46 | Some denominations have very low fractions.
47 | Storing them `int64` you would get.
48 |
49 | - `BTC` _satoshi_ is `1 BTC = 100,000,000 satoshi`, which is still enough for ~`92,233,720,368 BTC`.
50 | - `ETH` _wei_ is `1 ETH = 1,000,000,000,000,000,000 wei`, which is ~`9 ETH`. If you deal with _wei_, you may consider `bigint` or multiple `int64`. In fact, official Ethereum code is in Go and it is using bigint ([code](https://github.com/ethereum/go-ethereum/blob/master/params/denomination.go)).
51 |
52 | ### Benchmarks
53 |
54 | ```bash
55 | $ go test -bench=. -benchmem .
56 | goos: darwin
57 | goarch: arm64
58 | pkg: github.com/nikolaydubina/fpmoney
59 | cpu: Apple M3 Max
60 | BenchmarkCurrency_UnmarshalText-16 510934713 2.213 ns/op 0 B/op 0 allocs/op
61 | BenchmarkCurrency_AppendText-16 439866170 2.714 ns/op 0 B/op 0 allocs/op
62 | BenchmarkCurrency_MarshalText-16 88133492 13.52 ns/op 8 B/op 1 allocs/op
63 | BenchmarkCurrency_String-16 1000000000 1.078 ns/op 0 B/op 0 allocs/op
64 | BenchmarkArithmetic/add-16 901921378 1.562 ns/op 0 B/op 0 allocs/op
65 | BenchmarkJSON/small/encode-16 5652006 211.6 ns/op 160 B/op 3 allocs/op
66 | BenchmarkJSON/small/decode-16 4993570 236.0 ns/op 152 B/op 2 allocs/op
67 | BenchmarkJSON/large/encode-16 4835323 246.9 ns/op 176 B/op 3 allocs/op
68 | BenchmarkJSON/large/decode-16 3946946 304.9 ns/op 152 B/op 2 allocs/op
69 | PASS
70 | ok github.com/nikolaydubina/fpmoney 11.287s
71 | ```
72 |
73 | ## References and Related Work
74 |
75 | - [ferdypruis/iso4217](https://github.com/ferdypruis/iso4217) was a good inspiration and reference material. it was used in early version as well. it is well maintained and fast library for currencies.
76 | - `github.com/shopspring/decimal`: fixed precision; faster printing/parsing/arithmetics; currency handling
77 | - `github.com/Rhymond/go-money`: does not use `float` or `interface{}` in parsing; currency is enum
78 | - `github.com/ferdypruis/iso4217`: skipped deprecated currencies to fit into `uint8` and smaller struct size
79 | - https://en.wikipedia.org/wiki/ISO_4217
80 |
81 | [^1]: excluding currencies with 4+ minor units `CLF`, `UYW`
82 | [^2]: excluding deprecated currencies `HRD`, `HRK`, `SLL`, `ZWL`
83 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Reporting a Vulnerability
4 |
5 | Contact [@nikolaydubina](https://github.com/nikolaydubina) over email or linkedin.
6 |
--------------------------------------------------------------------------------
/amount.go:
--------------------------------------------------------------------------------
1 | package fpmoney
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/nikolaydubina/fpdecimal"
7 | )
8 |
9 | // Amount stores fixed-precision decimal money.
10 | // Stores integer number of cents for ISO 4217 currency.
11 | // Values fit in ~92 quadrillion for 2 decimal currencies.
12 | // Does not use float in printing nor parsing.
13 | // Rounds down fractional cents during parsing.
14 | // Blocking arithmetic operations that result in loss of precision.
15 | type Amount struct {
16 | v int64
17 | c Currency
18 | _ uint8 // padding for improved line alignment, 2x improves arithmetics
19 | _ uint32 // padding
20 | }
21 |
22 | func FromIntScaled[T ~int | ~int8 | ~int16 | ~int32 | ~int64](v T, currency Currency) Amount {
23 | return Amount{v: int64(v), c: currency}
24 | }
25 |
26 | func FromInt[T ~int | ~int8 | ~int16 | ~int32 | ~int64](v T, currency Currency) Amount {
27 | return Amount{v: int64(v) * int64(currency.scale()), c: currency}
28 | }
29 |
30 | func FromFloat[T ~float32 | ~float64](v T, currency Currency) Amount {
31 | return Amount{v: int64(T(v) * T(currency.scale())), c: currency}
32 | }
33 |
34 | func (a Amount) Float32() float32 { return float32(a.v) / float32(a.c.scale()) }
35 |
36 | func (a Amount) Float64() float64 { return float64(a.v) / float64(a.c.scale()) }
37 |
38 | func (a Amount) Currency() Currency { return a.c }
39 |
40 | func (a Amount) Scaled() int64 { return a.v }
41 |
42 | func (a Amount) Add(b Amount) Amount {
43 | checkCurrency(a.c, b.c)
44 | return Amount{v: a.v + b.v, c: a.c}
45 | }
46 |
47 | func (a Amount) Sub(b Amount) Amount {
48 | checkCurrency(a.c, b.c)
49 | return Amount{v: a.v - b.v, c: a.c}
50 | }
51 |
52 | func (a Amount) GreaterThan(b Amount) bool {
53 | checkCurrency(a.c, b.c)
54 | return a.v > b.v
55 | }
56 |
57 | func (a Amount) LessThan(b Amount) bool {
58 | checkCurrency(a.c, b.c)
59 | return a.v < b.v
60 | }
61 |
62 | func (a Amount) GreaterThanOrEqual(b Amount) bool {
63 | checkCurrency(a.c, b.c)
64 | return a.v >= b.v
65 | }
66 |
67 | func (a Amount) LessThanOrEqual(b Amount) bool {
68 | checkCurrency(a.c, b.c)
69 | return a.v <= b.v
70 | }
71 |
72 | func checkCurrency(a, b Currency) {
73 | if a != b {
74 | panic(&ErrCurrencyMismatch{A: a, B: b})
75 | }
76 | }
77 |
78 | func (a Amount) Mul(b int) Amount { return Amount{v: a.v * int64(b), c: a.c} }
79 |
80 | func (a Amount) DivMod(b int) (part Amount, remainder Amount) {
81 | return Amount{v: a.v / int64(b), c: a.c}, Amount{v: a.v % int64(b), c: a.c}
82 | }
83 |
84 | func (a Amount) StringPair() (amount, currency string) {
85 | return fpdecimal.FixedPointDecimalToString(a.v, a.c.Exponent()), a.c.String()
86 | }
87 |
88 | func (a Amount) String() string {
89 | return fpdecimal.FixedPointDecimalToString(a.v, a.c.Exponent()) + " " + a.c.String()
90 | }
91 |
92 | const (
93 | keyCurrency = "currency"
94 | keyAmount = "amount"
95 | lenISO427Currency = 3
96 | )
97 |
98 | // UnmarshalJSON parses string.
99 | // This is implemented directly for speed.
100 | // Avoiding json.Decoder, interface{}, reflect, tags, temporary structs.
101 | // Avoiding mallocs.
102 | // Go json package provides:
103 | // - check that pointer method receiver is not nil;
104 | // - removes whitespace in b []bytes
105 | func (a *Amount) UnmarshalJSON(b []byte) (err error) {
106 | var as, ae, e int
107 |
108 | for i := 0; i < len(b); i++ {
109 | // currency
110 | if b[i] == keyCurrency[0] && (i+len(keyCurrency)) <= len(b) && string(b[i:i+len(keyCurrency)]) == keyCurrency {
111 | i += len(keyCurrency) + 2 // `":`
112 |
113 | // find opening quote.
114 | for ; i < len(b) && b[i] != '"'; i++ {
115 | }
116 | if i == len(b) {
117 | return ErrWrongCurrencyString
118 | }
119 | i++ // opening `"`
120 | e = i + lenISO427Currency
121 | if e > len(b) {
122 | return ErrWrongCurrencyString
123 | }
124 |
125 | if err := a.c.UnmarshalText(b[i:e]); err != nil {
126 | return err
127 | }
128 | i = e
129 | }
130 |
131 | // amount
132 | if b[i] == keyAmount[0] && (i+len(keyCurrency)) <= len(b) && string(b[i:i+len(keyAmount)]) == keyAmount {
133 | i += len(keyAmount) + 2 // `":`
134 | // go until find either number or + or -, which is a start of simple number.
135 | for ; i < len(b) && !(b[i] >= '0' && b[i] <= '9') && b[i] != '-' && b[i] != '+'; i++ {
136 | }
137 | as = i
138 | // find end of number
139 | for ae = i; ae < len(b) && ((b[ae] >= '0' && b[ae] <= '9') || b[ae] == '-' || b[ae] == '+' || b[ae] == '.'); ae++ {
140 | }
141 | i = ae
142 | }
143 | }
144 |
145 | a.v, err = fpdecimal.ParseFixedPointDecimal(b[as:ae], a.c.Exponent())
146 |
147 | return err
148 | }
149 |
150 | func (a Amount) AppendJSON(b []byte) ([]byte, error) {
151 | b = append(b, `{"`...)
152 | b = append(b, keyAmount...)
153 | b = append(b, `":`...)
154 |
155 | b = fpdecimal.AppendFixedPointDecimal(b, a.v, a.c.Exponent())
156 |
157 | b = append(b, `,"`...)
158 | b = append(b, keyCurrency...)
159 | b = append(b, `":"`...)
160 |
161 | var err error
162 | b, err = a.c.AppendText(b)
163 |
164 | b = append(b, `"}`...)
165 | return b, err
166 | }
167 |
168 | func (a Amount) MarshalJSON() ([]byte, error) { return a.AppendJSON(make([]byte, 0, 100)) }
169 |
170 | var ErrWrongCurrencyString = errors.New("wrong currency string")
171 |
172 | type ErrCurrencyMismatch struct {
173 | A, B Currency
174 | }
175 |
176 | func NewErrCurrencyMismatch() *ErrCurrencyMismatch { return &ErrCurrencyMismatch{} }
177 |
178 | func (e *ErrCurrencyMismatch) Error() string { return e.A.String() + " != " + e.B.String() }
179 |
--------------------------------------------------------------------------------
/amount_test.go:
--------------------------------------------------------------------------------
1 | package fpmoney_test
2 |
3 | import (
4 | _ "embed"
5 | "encoding/json"
6 | "errors"
7 | "fmt"
8 | "log"
9 | "math/rand"
10 | "strconv"
11 | "strings"
12 | "testing"
13 |
14 | "github.com/nikolaydubina/fpmoney"
15 | )
16 |
17 | func ExampleAmount() {
18 | var BuySP500Price = fpmoney.FromInt(9000, fpmoney.SGD)
19 |
20 | input := []byte(`{"sp500": {"amount": 9000.02, "currency": "SGD"}}`)
21 |
22 | type Stonks struct {
23 | SP500 fpmoney.Amount `json:"sp500"`
24 | }
25 | var v Stonks
26 | if err := json.Unmarshal(input, &v); err != nil {
27 | log.Fatal(err)
28 | }
29 |
30 | amountToBuy := fpmoney.FromInt(0, fpmoney.SGD)
31 | if v.SP500.GreaterThan(BuySP500Price) {
32 | amountToBuy = amountToBuy.Add(v.SP500.Mul(2))
33 | }
34 |
35 | fmt.Println(amountToBuy)
36 | // Output: 18000.04 SGD
37 | }
38 |
39 | func ExampleAmount_StringPair() {
40 | amount := fpmoney.FromFloat(11.23, fpmoney.SGD)
41 |
42 | amountStr, currencyStr := amount.StringPair()
43 | fmt.Print(currencyStr, ":", amountStr)
44 | // Output: SGD:11.23
45 | }
46 |
47 | func ExampleAmount_DivMod_part() {
48 | x := fpmoney.FromInt(1, fpmoney.SGD)
49 | a, r := x.DivMod(3)
50 | fmt.Println(a, r)
51 | // Output: 0.33 SGD 0.01 SGD
52 | }
53 |
54 | func ExampleAmount_DivMod_whole() {
55 | x := fpmoney.FromInt(1, fpmoney.SGD)
56 | a, r := x.DivMod(5)
57 | fmt.Println(a, r)
58 | // Output: 0.2 SGD 0 SGD
59 | }
60 |
61 | func ExampleAmount_equality() {
62 | x := fpmoney.FromInt(3, fpmoney.SGD)
63 | y := fpmoney.FromInt(9, fpmoney.SGD)
64 | fmt.Println(y == x.Mul(3))
65 | // Output: true
66 | }
67 |
68 | func ExampleAmount_equality_same_currency() {
69 | x := fpmoney.FromInt(10, fpmoney.SGD)
70 | y := fpmoney.FromInt(10, fpmoney.SGD)
71 | fmt.Println(y == x)
72 | // Output: true
73 | }
74 |
75 | func ExampleAmount_equality_wrong_currency() {
76 | x := fpmoney.FromInt(10, fpmoney.USD)
77 | y := fpmoney.FromInt(10, fpmoney.SGD)
78 | fmt.Println(y == x)
79 | // Output: false
80 | }
81 |
82 | func ExampleFromFloat() {
83 | x := fpmoney.FromFloat(144.96, fpmoney.SGD)
84 | fmt.Println(x)
85 | // Output: 144.96 SGD
86 | }
87 |
88 | func ExampleAmount_Scaled_fractions() {
89 | v := fpmoney.FromFloat(42.23, fpmoney.EUR)
90 | fmt.Println(v.Scaled())
91 | // Output: 4223
92 | }
93 |
94 | func ExampleAmount_Scaled_many_fractions() {
95 | v := fpmoney.FromFloat(17.0, fpmoney.BHD)
96 | fmt.Println(v.Scaled())
97 | // Output: 17000
98 | }
99 |
100 | func ExampleAmount_Scaled_large() {
101 | v := fpmoney.FromFloat(8764534896.42, fpmoney.USD)
102 | fmt.Println(v.Scaled())
103 | // Output: 876453489642
104 | }
105 | func ExampleAmount_Scaled_whole() {
106 | v := fpmoney.FromFloat(23.0, fpmoney.EUR)
107 | fmt.Println(v.Scaled())
108 | // Output: 2300
109 | }
110 |
111 | func ExampleAmount_Scaled_from_scaled() {
112 | v := fpmoney.FromIntScaled(17, fpmoney.EUR)
113 | fmt.Println(v.Scaled())
114 | // Output: 17
115 | }
116 |
117 | func FuzzArithmetics(f *testing.F) {
118 | currencies := [...]fpmoney.Currency{
119 | fpmoney.KRW,
120 | fpmoney.SGD,
121 | fpmoney.BHD,
122 | }
123 |
124 | tests := [][2]int64{
125 | {1, 2},
126 | {1, -5},
127 | {1, 0},
128 | {1100, -2},
129 | }
130 | for _, tc := range tests {
131 | for i := range currencies {
132 | f.Add(tc[0], tc[1], i)
133 | }
134 | }
135 | f.Fuzz(func(t *testing.T, a, b int64, c int) {
136 | if c > len(currencies)-1 || c < 0 {
137 | t.Skip()
138 | }
139 | currency := currencies[c]
140 |
141 | fa := fpmoney.FromIntScaled(a, currency)
142 | fb := fpmoney.FromIntScaled(b, currency)
143 |
144 | v := []bool{
145 | // sum commutativity
146 | fa.Add(fb) == fb.Add(fa),
147 |
148 | // sum associativity
149 | fpmoney.FromInt(0, currency).Add(fa).Add(fb).Add(fa) == fpmoney.FromInt(0, currency).Add(fb).Add(fa).Add(fa),
150 |
151 | // sum zero
152 | fa == fa.Add(fb).Sub(fb),
153 | fa == fa.Sub(fb).Add(fb),
154 | fpmoney.FromInt(0, currency) == fpmoney.FromInt(0, currency).Add(fa).Sub(fa),
155 |
156 | // product identity
157 | fa == fa.Mul(1),
158 |
159 | // product zero
160 | fpmoney.FromInt(0, currency) == fa.Mul(0),
161 |
162 | // match number
163 | (a == b) == (fa == fb),
164 | a < b == fa.LessThan(fb),
165 | a > b == fa.GreaterThan(fb),
166 | a <= b == fa.LessThanOrEqual(fb),
167 | a >= b == fa.GreaterThanOrEqual(fb),
168 |
169 | // match number convert
170 | fpmoney.FromIntScaled(a+b, currency) == fa.Add(fb),
171 | fpmoney.FromIntScaled(a-b, currency) == fa.Sub(fb),
172 | }
173 | for i, q := range v {
174 | if !q {
175 | t.Error(i, a, b, fa, fb)
176 | }
177 | }
178 |
179 | if b != 0 {
180 | w, r := fa.DivMod(int(b))
181 | if w != fpmoney.FromIntScaled(a/b, currency) {
182 | t.Error(w, a/b, a, b, fa)
183 | }
184 | if r != fpmoney.FromIntScaled(a%b, currency) {
185 | t.Error(r, a%b, a, b, fa)
186 | }
187 | }
188 | })
189 | }
190 |
191 | func FuzzJSONUnmarshal_Float(f *testing.F) {
192 | currencies := [...]fpmoney.Currency{
193 | fpmoney.KRW,
194 | fpmoney.SGD,
195 | fpmoney.BHD,
196 | }
197 |
198 | tests := []float32{
199 | 0,
200 | 0.100,
201 | 0.101,
202 | 0.010,
203 | 0.001,
204 | 0.0001,
205 | 0.123,
206 | 0.103,
207 | 0.100001,
208 | 12.001,
209 | 12.010,
210 | 12.345,
211 | 1,
212 | 2,
213 | 10,
214 | 12345678,
215 | }
216 | for _, tc := range tests {
217 | for i := range currencies {
218 | f.Add(tc, i, uint8(5))
219 | f.Add(-tc, i, uint8(5))
220 | }
221 | }
222 | f.Fuzz(func(t *testing.T, r float32, c int, nf uint8) {
223 | if c > len(currencies)-1 || c < 0 {
224 | t.Skip()
225 | }
226 | if nf > 10 {
227 | t.Skip()
228 | }
229 | var l float32 = 10000000
230 | if r > l || r < -l {
231 | t.Skip()
232 | }
233 | if c == 0 {
234 | t.Skip()
235 | }
236 |
237 | currency := currencies[c]
238 |
239 | fs := `%.` + strconv.Itoa(int(nf)) + `f`
240 | rs := fmt.Sprintf(fs, r)
241 | s := fmt.Sprintf(`{"amount": %s, "currency": "%s"}`, rs, currency.String())
242 | if _, err := fmt.Sscanf(rs, "%f", &r); err != nil {
243 | t.Error(err)
244 | }
245 |
246 | if r == -0 {
247 | t.Skip()
248 | }
249 |
250 | var x fpmoney.Amount
251 | if err := json.Unmarshal([]byte(s), &x); err != nil {
252 | t.Error(rs, currency, err)
253 | }
254 |
255 | if x.Currency() != currency {
256 | t.Error(x, currency)
257 | }
258 | })
259 | }
260 |
261 | func FuzzJSONUnmarshal_NoPanic(f *testing.F) {
262 | amounts := []string{
263 | "123.456",
264 | "0.123",
265 | "0.1",
266 | "0.01",
267 | "0.001",
268 | "0.000",
269 | "0.123.2",
270 | "0..1",
271 | "0.1.2",
272 | "123.1o2",
273 | "--123",
274 | "00000.123",
275 | "-",
276 | "",
277 | "123456",
278 | }
279 | currencies := []string{
280 | "SGD",
281 | "SGDSGD",
282 | "",
283 | "123",
284 | "'SGD'",
285 | `"TDF"`,
286 | }
287 | for _, a := range amounts {
288 | for _, c := range currencies {
289 | f.Add(a)
290 | f.Add("-" + a)
291 | f.Add(fmt.Sprintf(`{"amount": %s, "currency": %s}`, a, c))
292 | f.Add(fmt.Sprintf(`{"amount": -%s, "currency": %s}`, a, c))
293 | f.Add(fmt.Sprintf(`{"amount": -%s, "currency": %s}`, a, c))
294 | f.Add(fmt.Sprintf(`"amount": -%s, "currency": %s`, a, c))
295 | f.Add(fmt.Sprintf(`{"amount": -%s}`, a))
296 | f.Add(fmt.Sprintf(`{"currency": %s}`, c))
297 | f.Add(fmt.Sprintf(`{"amount": %s, "currency": %s}`, c, a))
298 | f.Add(fmt.Sprintf(`"amount": %s, "currency": %s}`, a, c))
299 | f.Add(fmt.Sprintf(`{"amount": %s, "currency": %s`, c, a))
300 | f.Add(fmt.Sprintf(`{"amount": %s,,""""currency": %s}`, a, c))
301 | }
302 | }
303 |
304 | f.Add(`{"amount": 123.32, "currency":""}`)
305 | f.Add(`{"amount": , "currency":""}`)
306 | f.Add(`{"amount":,"currency":""}`)
307 |
308 | f.Fuzz(func(t *testing.T, s string) {
309 | var x fpmoney.Amount
310 | err := json.Unmarshal([]byte(s), &x)
311 | if err != nil {
312 | if (x != fpmoney.Amount{}) {
313 | t.Errorf("has to be 0 on error")
314 | }
315 | return
316 | }
317 | })
318 | }
319 |
320 | func FuzzToFloat(f *testing.F) {
321 | tests := []int64{
322 | 0,
323 | 1,
324 | 123456,
325 | }
326 | for _, tc := range tests {
327 | f.Add(tc)
328 | f.Add(-tc)
329 | }
330 | f.Fuzz(func(t *testing.T, v int64) {
331 | a := fpmoney.FromIntScaled(v, fpmoney.KRW)
332 |
333 | if float32(v) != a.Float32() {
334 | t.Error(a, a.Float32(), float32(v))
335 | }
336 |
337 | if float64(v) != a.Float64() {
338 | t.Error(a, a.Float64(), v)
339 | }
340 | })
341 | }
342 |
343 | func TestUnmarshalJSON(t *testing.T) {
344 | tests := []struct {
345 | s string
346 | v fpmoney.Amount
347 | }{
348 | // 2 cents
349 | {
350 | s: `{"currency": "SGD","amount": 9002.01}`,
351 | v: fpmoney.FromIntScaled(900201, fpmoney.SGD),
352 | },
353 | {
354 | s: `{"amount": 9002.01, "currency": "SGD"}`,
355 | v: fpmoney.FromIntScaled(900201, fpmoney.SGD),
356 | },
357 | {
358 | s: `{"amount": -9002.01, "currency": "SGD"}`,
359 | v: fpmoney.FromIntScaled(-900201, fpmoney.SGD),
360 | },
361 | {
362 | s: `{"amount": 0, "currency": "SGD"}`,
363 | v: fpmoney.FromIntScaled(0, fpmoney.SGD),
364 | },
365 | {
366 | s: `{"amount": 0.01, "currency": "SGD"}`,
367 | v: fpmoney.FromIntScaled(1, fpmoney.SGD),
368 | },
369 | {
370 | s: `{"amount": -0.01, "currency": "SGD"}`,
371 | v: fpmoney.FromIntScaled(-1, fpmoney.SGD),
372 | },
373 | // 0 cents
374 | {
375 | s: `{"amount":1,"currency":"KRW"}`,
376 | v: fpmoney.FromIntScaled(1, fpmoney.KRW),
377 | },
378 | {
379 | s: `{"amount":-1,"currency":"KRW"}`,
380 | v: fpmoney.FromIntScaled(-1, fpmoney.KRW),
381 | },
382 | {
383 | s: `{"amount":123,"currency":"KRW"}`,
384 | v: fpmoney.FromIntScaled(123, fpmoney.KRW),
385 | },
386 | {
387 | s: `{"amount":-123,"currency":"KRW"}`,
388 | v: fpmoney.FromIntScaled(-123, fpmoney.KRW),
389 | },
390 | // 2 cents strange valid input
391 | {
392 | s: ` { "amount" : 9002.01
393 |
394 | ,
395 |
396 | "currency"
397 | :
398 |
399 | "SGD"}
400 |
401 |
402 | `,
403 | v: fpmoney.FromIntScaled(900201, fpmoney.SGD),
404 | },
405 | }
406 | for _, tc := range tests {
407 | t.Run(tc.s, func(t *testing.T) {
408 | var v fpmoney.Amount
409 | err := json.Unmarshal([]byte(tc.s), &v)
410 | if err != nil {
411 | t.Error(err)
412 | }
413 | if tc.v != v {
414 | t.Error(tc.v, v)
415 | }
416 | })
417 | }
418 | }
419 |
420 | func TestMarshalJSON(t *testing.T) {
421 | tests := []struct {
422 | s string
423 | v fpmoney.Amount
424 | }{
425 | // 2 cents
426 | {
427 | s: `{"amount":9002.01,"currency":"SGD"}`,
428 | v: fpmoney.FromIntScaled(900201, fpmoney.SGD),
429 | },
430 | {
431 | s: `{"amount":0,"currency":"SGD"}`,
432 | v: fpmoney.FromIntScaled(0, fpmoney.SGD),
433 | },
434 | {
435 | s: `{"amount":0.01,"currency":"SGD"}`,
436 | v: fpmoney.FromIntScaled(1, fpmoney.SGD),
437 | },
438 | {
439 | s: `{"amount":-0.01,"currency":"SGD"}`,
440 | v: fpmoney.FromIntScaled(-1, fpmoney.SGD),
441 | },
442 | {
443 | s: `{"amount":1.01,"currency":"SGD"}`,
444 | v: fpmoney.FromIntScaled(101, fpmoney.SGD),
445 | },
446 | {
447 | s: `{"amount":-1.01,"currency":"SGD"}`,
448 | v: fpmoney.FromIntScaled(-101, fpmoney.SGD),
449 | },
450 | {
451 | s: `{"amount":1,"currency":"SGD"}`,
452 | v: fpmoney.FromInt(1, fpmoney.SGD),
453 | },
454 | {
455 | s: `{"amount":5,"currency":"SGD"}`,
456 | v: fpmoney.FromInt(5, fpmoney.SGD),
457 | },
458 | {
459 | s: `{"amount":-1,"currency":"SGD"}`,
460 | v: fpmoney.FromInt(-1, fpmoney.SGD),
461 | },
462 | {
463 | s: `{"amount":-5,"currency":"SGD"}`,
464 | v: fpmoney.FromInt(-5, fpmoney.SGD),
465 | },
466 | // 0 cents
467 | {
468 | s: `{"amount":1,"currency":"KRW"}`,
469 | v: fpmoney.FromIntScaled(1, fpmoney.KRW),
470 | },
471 | {
472 | s: `{"amount":-1,"currency":"KRW"}`,
473 | v: fpmoney.FromIntScaled(-1, fpmoney.KRW),
474 | },
475 | {
476 | s: `{"amount":123,"currency":"KRW"}`,
477 | v: fpmoney.FromIntScaled(123, fpmoney.KRW),
478 | },
479 | {
480 | s: `{"amount":-123,"currency":"KRW"}`,
481 | v: fpmoney.FromIntScaled(-123, fpmoney.KRW),
482 | },
483 | }
484 | for _, tc := range tests {
485 | t.Run(tc.s, func(t *testing.T) {
486 | s, err := json.Marshal(tc.v)
487 | if err != nil {
488 | t.Error(err)
489 | }
490 | if tc.s != string(s) {
491 | t.Error(tc.s, string(s))
492 | }
493 | })
494 | }
495 | }
496 |
497 | func FuzzJSON_MarshalUnmarshal(f *testing.F) {
498 | currencies := [...]fpmoney.Currency{
499 | fpmoney.KRW,
500 | fpmoney.SGD,
501 | fpmoney.BHD,
502 | }
503 |
504 | tests := []int64{
505 | 123456,
506 | 0,
507 | 1,
508 | }
509 | for _, tc := range tests {
510 | for i := range currencies {
511 | f.Add(tc, i)
512 | f.Add(-tc, i)
513 | }
514 | }
515 | f.Fuzz(func(t *testing.T, v int64, c int) {
516 | if c > len(currencies)-1 || c < 0 {
517 | t.Skip()
518 | }
519 |
520 | currency := currencies[c]
521 | q := fpmoney.FromIntScaled(v, currency)
522 |
523 | s, err := json.Marshal(q)
524 | if err != nil {
525 | t.Error(err)
526 | }
527 |
528 | var x fpmoney.Amount
529 | if err := json.Unmarshal(s, &x); err != nil {
530 | t.Error(err, string(s))
531 | }
532 |
533 | if x != q {
534 | t.Error(x, q, v, c, s)
535 | }
536 | })
537 | }
538 |
539 | func BenchmarkArithmetic(b *testing.B) {
540 | x := fpmoney.FromFloat(rand.Float32(), fpmoney.SGD)
541 | y := fpmoney.FromFloat(rand.Float32(), fpmoney.SGD)
542 |
543 | b.Run("add", func(b *testing.B) {
544 | for b.Loop() {
545 | x.Add(y)
546 | }
547 | })
548 | }
549 |
550 | //go:embed testdata/amount-float-large.jsonl
551 | var amountFloatLargeJSONL string
552 |
553 | //go:embed testdata/amount-float-small.jsonl
554 | var amountFloatSmallJSONL string
555 |
556 | var testsFloats = []struct {
557 | name string
558 | vals []string
559 | }{
560 | {
561 | name: "small",
562 | vals: strings.Split(amountFloatSmallJSONL, "\n"),
563 | },
564 | {
565 | name: "large",
566 | vals: strings.Split(amountFloatLargeJSONL, "\n"),
567 | },
568 | }
569 |
570 | func BenchmarkJSON(b *testing.B) {
571 | for _, tc := range testsFloats {
572 | b.Run(tc.name, func(b *testing.B) {
573 | tests := make([]fpmoney.Amount, 0, len(tc.vals))
574 | for _, q := range tc.vals {
575 | var x fpmoney.Amount
576 | if err := json.Unmarshal([]byte(q), &x); err != nil {
577 | b.Error(err)
578 | }
579 | tests = append(tests, x)
580 | tests = append(tests, x.Mul(-1))
581 | }
582 |
583 | b.Run("encode", func(b *testing.B) {
584 | v := tests[rand.Intn(len(tests))]
585 |
586 | for b.Loop() {
587 | _, _ = json.Marshal(v)
588 | }
589 | })
590 |
591 | b.Run("decode", func(b *testing.B) {
592 | var s fpmoney.Amount
593 | v := []byte(tc.vals[rand.Intn(len(tc.vals))])
594 |
595 | for b.Loop() {
596 | json.Unmarshal(v, &s)
597 | }
598 | })
599 | })
600 | }
601 | }
602 |
603 | func TestArithmetic_WrongCurrency(t *testing.T) {
604 | tests := []struct {
605 | a fpmoney.Amount
606 | b fpmoney.Amount
607 | f func(a, b fpmoney.Amount)
608 | e *fpmoney.ErrCurrencyMismatch
609 | }{
610 | {
611 | a: fpmoney.FromInt(10, fpmoney.SGD),
612 | b: fpmoney.FromInt(11, fpmoney.USD),
613 | f: func(a, b fpmoney.Amount) { a.Add(b) },
614 | e: &fpmoney.ErrCurrencyMismatch{A: fpmoney.SGD, B: fpmoney.USD},
615 | },
616 | {
617 | a: fpmoney.FromInt(10, fpmoney.SGD),
618 | b: fpmoney.FromInt(11, fpmoney.USD),
619 | f: func(a, b fpmoney.Amount) { a.Sub(b) },
620 | e: &fpmoney.ErrCurrencyMismatch{A: fpmoney.SGD, B: fpmoney.USD},
621 | },
622 | {
623 | a: fpmoney.FromInt(10, fpmoney.SGD),
624 | b: fpmoney.FromInt(11, fpmoney.USD),
625 | f: func(a, b fpmoney.Amount) { a.LessThan(b) },
626 | e: &fpmoney.ErrCurrencyMismatch{A: fpmoney.SGD, B: fpmoney.USD},
627 | },
628 | {
629 | a: fpmoney.FromInt(10, fpmoney.SGD),
630 | b: fpmoney.FromInt(11, fpmoney.USD),
631 | f: func(a, b fpmoney.Amount) { a.LessThanOrEqual(b) },
632 | e: &fpmoney.ErrCurrencyMismatch{A: fpmoney.SGD, B: fpmoney.USD},
633 | },
634 | {
635 | a: fpmoney.FromInt(10, fpmoney.SGD),
636 | b: fpmoney.FromInt(11, fpmoney.USD),
637 | f: func(a, b fpmoney.Amount) { a.GreaterThan(b) },
638 | e: &fpmoney.ErrCurrencyMismatch{A: fpmoney.SGD, B: fpmoney.USD},
639 | },
640 | {
641 | a: fpmoney.FromInt(10, fpmoney.SGD),
642 | b: fpmoney.FromInt(11, fpmoney.USD),
643 | f: func(a, b fpmoney.Amount) { a.GreaterThanOrEqual(b) },
644 | e: &fpmoney.ErrCurrencyMismatch{A: fpmoney.SGD, B: fpmoney.USD},
645 | },
646 | }
647 | for i, tc := range tests {
648 | t.Run(strconv.Itoa(i), func(t *testing.T) {
649 | defer func() {
650 | r := recover()
651 | re, ok := r.(error)
652 | if !ok {
653 | t.Error(r)
654 | }
655 | if err := fpmoney.NewErrCurrencyMismatch(); !errors.As(re, &err) || *err != *tc.e {
656 | t.Error(re, tc.e)
657 | }
658 | }()
659 | tc.f(tc.a, tc.b)
660 | })
661 | }
662 | }
663 |
664 | func TestErrCurrencyMismatch_Error(t *testing.T) {
665 | t.Run("error", func(t *testing.T) {
666 | e := &fpmoney.ErrCurrencyMismatch{A: fpmoney.SGD, B: fpmoney.USD}
667 | if e.Error() != "SGD != USD" {
668 | t.Error(e)
669 | }
670 | })
671 | }
672 |
--------------------------------------------------------------------------------
/currency.go:
--------------------------------------------------------------------------------
1 | package fpmoney
2 |
3 | // Currency is ISO 4217 without deprecated currencies.
4 | // - excluding currencies with 4+ minor units `CLF`, `UYW`
5 | // - excluding deprecated currencies `HRD`, `HRK`, `SLL`, `ZWL`
6 | type Currency uint8
7 |
8 | // Exponent returns the decimal point location.
9 | func (c Currency) Exponent() uint8 {
10 | switch c {
11 | case BIF, CLP, DJF, GNF, ISK, JPY, KMF, KRW, PYG, RWF, UGX, UYI, VND, VUV, XAF, XAG, XAU, XBA, XBB, XBC, XBD, XDR, XOF, XPD, XPF, XPT, XSU, XTS, XUA, XXX:
12 | return 0
13 | case BHD, IQD, JOD, KWD, LYD, OMR, TND:
14 | return 3
15 | default:
16 | return 2
17 | }
18 | }
19 |
20 | func (c Currency) scale() int64 {
21 | switch c.Exponent() {
22 | case 3:
23 | return 1000
24 | case 2:
25 | return 100
26 | default:
27 | return 1
28 | }
29 | }
30 |
31 | //go:generate go-enum-encoding -type=Currency -string
32 | const (
33 | UndefinedCurrency Currency = iota //
34 | AED // json:"AED"
35 | AFN // json:"AFN"
36 | ALL // json:"ALL"
37 | AMD // json:"AMD"
38 | ANG // json:"ANG"
39 | AOA // json:"AOA"
40 | ARS // json:"ARS"
41 | AUD // json:"AUD"
42 | AWG // json:"AWG"
43 | AZN // json:"AZN"
44 | BAM // json:"BAM"
45 | BBD // json:"BBD"
46 | BDT // json:"BDT"
47 | BGN // json:"BGN"
48 | BHD // json:"BHD"
49 | BIF // json:"BIF"
50 | BMD // json:"BMD"
51 | BND // json:"BND"
52 | BOB // json:"BOB"
53 | BOV // json:"BOV"
54 | BRL // json:"BRL"
55 | BSD // json:"BSD"
56 | BTN // json:"BTN"
57 | BWP // json:"BWP"
58 | BYN // json:"BYN"
59 | BZD // json:"BZD"
60 | CAD // json:"CAD"
61 | CDF // json:"CDF"
62 | CHE // json:"CHE"
63 | CHF // json:"CHF"
64 | CHW // json:"CHW"
65 | CLP // json:"CLP"
66 | CNY // json:"CNY"
67 | COP // json:"COP"
68 | COU // json:"COU"
69 | CRC // json:"CRC"
70 | CUP // json:"CUP"
71 | CVE // json:"CVE"
72 | CZK // json:"CZK"
73 | DJF // json:"DJF"
74 | DKK // json:"DKK"
75 | DOP // json:"DOP"
76 | DZD // json:"DZD"
77 | EGP // json:"EGP"
78 | ERN // json:"ERN"
79 | ETB // json:"ETB"
80 | EUR // json:"EUR"
81 | FJD // json:"FJD"
82 | FKP // json:"FKP"
83 | GBP // json:"GBP"
84 | GEL // json:"GEL"
85 | GHS // json:"GHS"
86 | GIP // json:"GIP"
87 | GMD // json:"GMD"
88 | GNF // json:"GNF"
89 | GTQ // json:"GTQ"
90 | GYD // json:"GYD"
91 | HKD // json:"HKD"
92 | HNL // json:"HNL"
93 | HRD // json:"HRD"
94 | HTG // json:"HTG"
95 | HUF // json:"HUF"
96 | IDR // json:"IDR"
97 | ILS // json:"ILS"
98 | INR // json:"INR"
99 | IQD // json:"IQD"
100 | IRR // json:"IRR"
101 | ISK // json:"ISK"
102 | JMD // json:"JMD"
103 | JOD // json:"JOD"
104 | JPY // json:"JPY"
105 | KES // json:"KES"
106 | KGS // json:"KGS"
107 | KHR // json:"KHR"
108 | KMF // json:"KMF"
109 | KPW // json:"KPW"
110 | KRW // json:"KRW"
111 | KWD // json:"KWD"
112 | KYD // json:"KYD"
113 | KZT // json:"KZT"
114 | LAK // json:"LAK"
115 | LBP // json:"LBP"
116 | LKR // json:"LKR"
117 | LRD // json:"LRD"
118 | LSL // json:"LSL"
119 | LYD // json:"LYD"
120 | MAD // json:"MAD"
121 | MDL // json:"MDL"
122 | MGA // json:"MGA"
123 | MKD // json:"MKD"
124 | MMK // json:"MMK"
125 | MNT // json:"MNT"
126 | MOP // json:"MOP"
127 | MRU // json:"MRU"
128 | MUR // json:"MUR"
129 | MVR // json:"MVR"
130 | MWK // json:"MWK"
131 | MXN // json:"MXN"
132 | MXV // json:"MXV"
133 | MYR // json:"MYR"
134 | MZN // json:"MZN"
135 | NAD // json:"NAD"
136 | NGN // json:"NGN"
137 | NIO // json:"NIO"
138 | NOK // json:"NOK"
139 | NPR // json:"NPR"
140 | NZD // json:"NZD"
141 | OMR // json:"OMR"
142 | PAB // json:"PAB"
143 | PEN // json:"PEN"
144 | PGK // json:"PGK"
145 | PHP // json:"PHP"
146 | PKR // json:"PKR"
147 | PLN // json:"PLN"
148 | PYG // json:"PYG"
149 | QAR // json:"QAR"
150 | RON // json:"RON"
151 | RSD // json:"RSD"
152 | RUB // json:"RUB"
153 | RWF // json:"RWF"
154 | SAR // json:"SAR"
155 | SBD // json:"SBD"
156 | SCR // json:"SCR"
157 | SDG // json:"SDG"
158 | SEK // json:"SEK"
159 | SGD // json:"SGD"
160 | SHP // json:"SHP"
161 | SLE // json:"SLE"
162 | SOS // json:"SOS"
163 | SRD // json:"SRD"
164 | SSP // json:"SSP"
165 | STN // json:"STN"
166 | SVC // json:"SVC"
167 | SYP // json:"SYP"
168 | SZL // json:"SZL"
169 | THB // json:"THB"
170 | TJS // json:"TJS"
171 | TMT // json:"TMT"
172 | TND // json:"TND"
173 | TOP // json:"TOP"
174 | TRY // json:"TRY"
175 | TTD // json:"TTD"
176 | TWD // json:"TWD"
177 | TZS // json:"TZS"
178 | UAH // json:"UAH"
179 | UGX // json:"UGX"
180 | USD // json:"USD"
181 | USN // json:"USN"
182 | UYI // json:"UYI"
183 | UYU // json:"UYU"
184 | UZS // json:"UZS"
185 | VED // json:"VED"
186 | VES // json:"VES"
187 | VND // json:"VND"
188 | VUV // json:"VUV"
189 | WST // json:"WST"
190 | XAF // json:"XAF"
191 | XAG // json:"XAG"
192 | XAU // json:"XAU"
193 | XBA // json:"XBA"
194 | XBB // json:"XBB"
195 | XBC // json:"XBC"
196 | XBD // json:"XBD"
197 | XCD // json:"XCD"
198 | XDR // json:"XDR"
199 | XOF // json:"XOF"
200 | XPD // json:"XPD"
201 | XPF // json:"XPF"
202 | XPT // json:"XPT"
203 | XSU // json:"XSU"
204 | XTS // json:"XTS"
205 | XUA // json:"XUA"
206 | XXX // json:"XXX"
207 | YER // json:"YER"
208 | ZAR // json:"ZAR"
209 | ZMW // json:"ZMW"
210 | ZWG // json:"ZWG"
211 | )
212 |
213 | var Currencies = [...]Currency{
214 | AED,
215 | AFN,
216 | ALL,
217 | AMD,
218 | ANG,
219 | AOA,
220 | ARS,
221 | AUD,
222 | AWG,
223 | AZN,
224 | BAM,
225 | BBD,
226 | BDT,
227 | BGN,
228 | BHD,
229 | BIF,
230 | BMD,
231 | BND,
232 | BOB,
233 | BOV,
234 | BRL,
235 | BSD,
236 | BTN,
237 | BWP,
238 | BYN,
239 | BZD,
240 | CAD,
241 | CDF,
242 | CHE,
243 | CHF,
244 | CHW,
245 | CLP,
246 | CNY,
247 | COP,
248 | COU,
249 | CRC,
250 | CUP,
251 | CVE,
252 | CZK,
253 | DJF,
254 | DKK,
255 | DOP,
256 | DZD,
257 | EGP,
258 | ERN,
259 | ETB,
260 | EUR,
261 | FJD,
262 | FKP,
263 | GBP,
264 | GEL,
265 | GHS,
266 | GIP,
267 | GMD,
268 | GNF,
269 | GTQ,
270 | GYD,
271 | HKD,
272 | HNL,
273 | HRD,
274 | HTG,
275 | HUF,
276 | IDR,
277 | ILS,
278 | INR,
279 | IQD,
280 | IRR,
281 | ISK,
282 | JMD,
283 | JOD,
284 | JPY,
285 | KES,
286 | KGS,
287 | KHR,
288 | KMF,
289 | KPW,
290 | KRW,
291 | KWD,
292 | KYD,
293 | KZT,
294 | LAK,
295 | LBP,
296 | LKR,
297 | LRD,
298 | LSL,
299 | LYD,
300 | MAD,
301 | MDL,
302 | MGA,
303 | MKD,
304 | MMK,
305 | MNT,
306 | MOP,
307 | MRU,
308 | MUR,
309 | MVR,
310 | MWK,
311 | MXN,
312 | MXV,
313 | MYR,
314 | MZN,
315 | NAD,
316 | NGN,
317 | NIO,
318 | NOK,
319 | NPR,
320 | NZD,
321 | OMR,
322 | PAB,
323 | PEN,
324 | PGK,
325 | PHP,
326 | PKR,
327 | PLN,
328 | PYG,
329 | QAR,
330 | RON,
331 | RSD,
332 | RUB,
333 | RWF,
334 | SAR,
335 | SBD,
336 | SCR,
337 | SDG,
338 | SEK,
339 | SGD,
340 | SHP,
341 | SLE,
342 | SOS,
343 | SRD,
344 | SSP,
345 | STN,
346 | SVC,
347 | SYP,
348 | SZL,
349 | THB,
350 | TJS,
351 | TMT,
352 | TND,
353 | TOP,
354 | TRY,
355 | TTD,
356 | TWD,
357 | TZS,
358 | UAH,
359 | UGX,
360 | USD,
361 | USN,
362 | UYI,
363 | UYU,
364 | UZS,
365 | VED,
366 | VES,
367 | VND,
368 | VUV,
369 | WST,
370 | XAF,
371 | XAG,
372 | XAU,
373 | XBA,
374 | XBB,
375 | XBC,
376 | XBD,
377 | XCD,
378 | XDR,
379 | XOF,
380 | XPD,
381 | XPF,
382 | XPT,
383 | XSU,
384 | XTS,
385 | XUA,
386 | XXX,
387 | YER,
388 | ZAR,
389 | ZMW,
390 | ZWG,
391 | }
392 |
--------------------------------------------------------------------------------
/currency_enum_encoding.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-enum-encoding; DO NOT EDIT.
2 |
3 | package fpmoney
4 |
5 | import "errors"
6 |
7 | var ErrUnknownCurrency = errors.New("unknown Currency")
8 |
9 | func (s *Currency) UnmarshalText(text []byte) error {
10 | switch string(text) {
11 | case "AED":
12 | *s = AED
13 | case "AFN":
14 | *s = AFN
15 | case "ALL":
16 | *s = ALL
17 | case "AMD":
18 | *s = AMD
19 | case "ANG":
20 | *s = ANG
21 | case "AOA":
22 | *s = AOA
23 | case "ARS":
24 | *s = ARS
25 | case "AUD":
26 | *s = AUD
27 | case "AWG":
28 | *s = AWG
29 | case "AZN":
30 | *s = AZN
31 | case "BAM":
32 | *s = BAM
33 | case "BBD":
34 | *s = BBD
35 | case "BDT":
36 | *s = BDT
37 | case "BGN":
38 | *s = BGN
39 | case "BHD":
40 | *s = BHD
41 | case "BIF":
42 | *s = BIF
43 | case "BMD":
44 | *s = BMD
45 | case "BND":
46 | *s = BND
47 | case "BOB":
48 | *s = BOB
49 | case "BOV":
50 | *s = BOV
51 | case "BRL":
52 | *s = BRL
53 | case "BSD":
54 | *s = BSD
55 | case "BTN":
56 | *s = BTN
57 | case "BWP":
58 | *s = BWP
59 | case "BYN":
60 | *s = BYN
61 | case "BZD":
62 | *s = BZD
63 | case "CAD":
64 | *s = CAD
65 | case "CDF":
66 | *s = CDF
67 | case "CHE":
68 | *s = CHE
69 | case "CHF":
70 | *s = CHF
71 | case "CHW":
72 | *s = CHW
73 | case "CLP":
74 | *s = CLP
75 | case "CNY":
76 | *s = CNY
77 | case "COP":
78 | *s = COP
79 | case "COU":
80 | *s = COU
81 | case "CRC":
82 | *s = CRC
83 | case "CUP":
84 | *s = CUP
85 | case "CVE":
86 | *s = CVE
87 | case "CZK":
88 | *s = CZK
89 | case "DJF":
90 | *s = DJF
91 | case "DKK":
92 | *s = DKK
93 | case "DOP":
94 | *s = DOP
95 | case "DZD":
96 | *s = DZD
97 | case "EGP":
98 | *s = EGP
99 | case "ERN":
100 | *s = ERN
101 | case "ETB":
102 | *s = ETB
103 | case "EUR":
104 | *s = EUR
105 | case "FJD":
106 | *s = FJD
107 | case "FKP":
108 | *s = FKP
109 | case "GBP":
110 | *s = GBP
111 | case "GEL":
112 | *s = GEL
113 | case "GHS":
114 | *s = GHS
115 | case "GIP":
116 | *s = GIP
117 | case "GMD":
118 | *s = GMD
119 | case "GNF":
120 | *s = GNF
121 | case "GTQ":
122 | *s = GTQ
123 | case "GYD":
124 | *s = GYD
125 | case "HKD":
126 | *s = HKD
127 | case "HNL":
128 | *s = HNL
129 | case "HRD":
130 | *s = HRD
131 | case "HTG":
132 | *s = HTG
133 | case "HUF":
134 | *s = HUF
135 | case "IDR":
136 | *s = IDR
137 | case "ILS":
138 | *s = ILS
139 | case "INR":
140 | *s = INR
141 | case "IQD":
142 | *s = IQD
143 | case "IRR":
144 | *s = IRR
145 | case "ISK":
146 | *s = ISK
147 | case "JMD":
148 | *s = JMD
149 | case "JOD":
150 | *s = JOD
151 | case "JPY":
152 | *s = JPY
153 | case "KES":
154 | *s = KES
155 | case "KGS":
156 | *s = KGS
157 | case "KHR":
158 | *s = KHR
159 | case "KMF":
160 | *s = KMF
161 | case "KPW":
162 | *s = KPW
163 | case "KRW":
164 | *s = KRW
165 | case "KWD":
166 | *s = KWD
167 | case "KYD":
168 | *s = KYD
169 | case "KZT":
170 | *s = KZT
171 | case "LAK":
172 | *s = LAK
173 | case "LBP":
174 | *s = LBP
175 | case "LKR":
176 | *s = LKR
177 | case "LRD":
178 | *s = LRD
179 | case "LSL":
180 | *s = LSL
181 | case "LYD":
182 | *s = LYD
183 | case "MAD":
184 | *s = MAD
185 | case "MDL":
186 | *s = MDL
187 | case "MGA":
188 | *s = MGA
189 | case "MKD":
190 | *s = MKD
191 | case "MMK":
192 | *s = MMK
193 | case "MNT":
194 | *s = MNT
195 | case "MOP":
196 | *s = MOP
197 | case "MRU":
198 | *s = MRU
199 | case "MUR":
200 | *s = MUR
201 | case "MVR":
202 | *s = MVR
203 | case "MWK":
204 | *s = MWK
205 | case "MXN":
206 | *s = MXN
207 | case "MXV":
208 | *s = MXV
209 | case "MYR":
210 | *s = MYR
211 | case "MZN":
212 | *s = MZN
213 | case "NAD":
214 | *s = NAD
215 | case "NGN":
216 | *s = NGN
217 | case "NIO":
218 | *s = NIO
219 | case "NOK":
220 | *s = NOK
221 | case "NPR":
222 | *s = NPR
223 | case "NZD":
224 | *s = NZD
225 | case "OMR":
226 | *s = OMR
227 | case "PAB":
228 | *s = PAB
229 | case "PEN":
230 | *s = PEN
231 | case "PGK":
232 | *s = PGK
233 | case "PHP":
234 | *s = PHP
235 | case "PKR":
236 | *s = PKR
237 | case "PLN":
238 | *s = PLN
239 | case "PYG":
240 | *s = PYG
241 | case "QAR":
242 | *s = QAR
243 | case "RON":
244 | *s = RON
245 | case "RSD":
246 | *s = RSD
247 | case "RUB":
248 | *s = RUB
249 | case "RWF":
250 | *s = RWF
251 | case "SAR":
252 | *s = SAR
253 | case "SBD":
254 | *s = SBD
255 | case "SCR":
256 | *s = SCR
257 | case "SDG":
258 | *s = SDG
259 | case "SEK":
260 | *s = SEK
261 | case "SGD":
262 | *s = SGD
263 | case "SHP":
264 | *s = SHP
265 | case "SLE":
266 | *s = SLE
267 | case "SOS":
268 | *s = SOS
269 | case "SRD":
270 | *s = SRD
271 | case "SSP":
272 | *s = SSP
273 | case "STN":
274 | *s = STN
275 | case "SVC":
276 | *s = SVC
277 | case "SYP":
278 | *s = SYP
279 | case "SZL":
280 | *s = SZL
281 | case "THB":
282 | *s = THB
283 | case "TJS":
284 | *s = TJS
285 | case "TMT":
286 | *s = TMT
287 | case "TND":
288 | *s = TND
289 | case "TOP":
290 | *s = TOP
291 | case "TRY":
292 | *s = TRY
293 | case "TTD":
294 | *s = TTD
295 | case "TWD":
296 | *s = TWD
297 | case "TZS":
298 | *s = TZS
299 | case "UAH":
300 | *s = UAH
301 | case "UGX":
302 | *s = UGX
303 | case "USD":
304 | *s = USD
305 | case "USN":
306 | *s = USN
307 | case "UYI":
308 | *s = UYI
309 | case "UYU":
310 | *s = UYU
311 | case "UZS":
312 | *s = UZS
313 | case "VED":
314 | *s = VED
315 | case "VES":
316 | *s = VES
317 | case "VND":
318 | *s = VND
319 | case "VUV":
320 | *s = VUV
321 | case "WST":
322 | *s = WST
323 | case "XAF":
324 | *s = XAF
325 | case "XAG":
326 | *s = XAG
327 | case "XAU":
328 | *s = XAU
329 | case "XBA":
330 | *s = XBA
331 | case "XBB":
332 | *s = XBB
333 | case "XBC":
334 | *s = XBC
335 | case "XBD":
336 | *s = XBD
337 | case "XCD":
338 | *s = XCD
339 | case "XDR":
340 | *s = XDR
341 | case "XOF":
342 | *s = XOF
343 | case "XPD":
344 | *s = XPD
345 | case "XPF":
346 | *s = XPF
347 | case "XPT":
348 | *s = XPT
349 | case "XSU":
350 | *s = XSU
351 | case "XTS":
352 | *s = XTS
353 | case "XUA":
354 | *s = XUA
355 | case "XXX":
356 | *s = XXX
357 | case "YER":
358 | *s = YER
359 | case "ZAR":
360 | *s = ZAR
361 | case "ZMW":
362 | *s = ZMW
363 | case "ZWG":
364 | *s = ZWG
365 | default:
366 | return ErrUnknownCurrency
367 | }
368 | return nil
369 | }
370 |
371 | var seq_bytes_Currency = [...][]byte{[]byte("AED"), []byte("AFN"), []byte("ALL"), []byte("AMD"), []byte("ANG"), []byte("AOA"), []byte("ARS"), []byte("AUD"), []byte("AWG"), []byte("AZN"), []byte("BAM"), []byte("BBD"), []byte("BDT"), []byte("BGN"), []byte("BHD"), []byte("BIF"), []byte("BMD"), []byte("BND"), []byte("BOB"), []byte("BOV"), []byte("BRL"), []byte("BSD"), []byte("BTN"), []byte("BWP"), []byte("BYN"), []byte("BZD"), []byte("CAD"), []byte("CDF"), []byte("CHE"), []byte("CHF"), []byte("CHW"), []byte("CLP"), []byte("CNY"), []byte("COP"), []byte("COU"), []byte("CRC"), []byte("CUP"), []byte("CVE"), []byte("CZK"), []byte("DJF"), []byte("DKK"), []byte("DOP"), []byte("DZD"), []byte("EGP"), []byte("ERN"), []byte("ETB"), []byte("EUR"), []byte("FJD"), []byte("FKP"), []byte("GBP"), []byte("GEL"), []byte("GHS"), []byte("GIP"), []byte("GMD"), []byte("GNF"), []byte("GTQ"), []byte("GYD"), []byte("HKD"), []byte("HNL"), []byte("HRD"), []byte("HTG"), []byte("HUF"), []byte("IDR"), []byte("ILS"), []byte("INR"), []byte("IQD"), []byte("IRR"), []byte("ISK"), []byte("JMD"), []byte("JOD"), []byte("JPY"), []byte("KES"), []byte("KGS"), []byte("KHR"), []byte("KMF"), []byte("KPW"), []byte("KRW"), []byte("KWD"), []byte("KYD"), []byte("KZT"), []byte("LAK"), []byte("LBP"), []byte("LKR"), []byte("LRD"), []byte("LSL"), []byte("LYD"), []byte("MAD"), []byte("MDL"), []byte("MGA"), []byte("MKD"), []byte("MMK"), []byte("MNT"), []byte("MOP"), []byte("MRU"), []byte("MUR"), []byte("MVR"), []byte("MWK"), []byte("MXN"), []byte("MXV"), []byte("MYR"), []byte("MZN"), []byte("NAD"), []byte("NGN"), []byte("NIO"), []byte("NOK"), []byte("NPR"), []byte("NZD"), []byte("OMR"), []byte("PAB"), []byte("PEN"), []byte("PGK"), []byte("PHP"), []byte("PKR"), []byte("PLN"), []byte("PYG"), []byte("QAR"), []byte("RON"), []byte("RSD"), []byte("RUB"), []byte("RWF"), []byte("SAR"), []byte("SBD"), []byte("SCR"), []byte("SDG"), []byte("SEK"), []byte("SGD"), []byte("SHP"), []byte("SLE"), []byte("SOS"), []byte("SRD"), []byte("SSP"), []byte("STN"), []byte("SVC"), []byte("SYP"), []byte("SZL"), []byte("THB"), []byte("TJS"), []byte("TMT"), []byte("TND"), []byte("TOP"), []byte("TRY"), []byte("TTD"), []byte("TWD"), []byte("TZS"), []byte("UAH"), []byte("UGX"), []byte("USD"), []byte("USN"), []byte("UYI"), []byte("UYU"), []byte("UZS"), []byte("VED"), []byte("VES"), []byte("VND"), []byte("VUV"), []byte("WST"), []byte("XAF"), []byte("XAG"), []byte("XAU"), []byte("XBA"), []byte("XBB"), []byte("XBC"), []byte("XBD"), []byte("XCD"), []byte("XDR"), []byte("XOF"), []byte("XPD"), []byte("XPF"), []byte("XPT"), []byte("XSU"), []byte("XTS"), []byte("XUA"), []byte("XXX"), []byte("YER"), []byte("ZAR"), []byte("ZMW"), []byte("ZWG")}
372 |
373 | func (s Currency) MarshalText() ([]byte, error) { return s.AppendText(nil) }
374 |
375 | func (s Currency) AppendText(b []byte) ([]byte, error) {
376 | switch s {
377 | case AED:
378 | return append(b, seq_bytes_Currency[0]...), nil
379 | case AFN:
380 | return append(b, seq_bytes_Currency[1]...), nil
381 | case ALL:
382 | return append(b, seq_bytes_Currency[2]...), nil
383 | case AMD:
384 | return append(b, seq_bytes_Currency[3]...), nil
385 | case ANG:
386 | return append(b, seq_bytes_Currency[4]...), nil
387 | case AOA:
388 | return append(b, seq_bytes_Currency[5]...), nil
389 | case ARS:
390 | return append(b, seq_bytes_Currency[6]...), nil
391 | case AUD:
392 | return append(b, seq_bytes_Currency[7]...), nil
393 | case AWG:
394 | return append(b, seq_bytes_Currency[8]...), nil
395 | case AZN:
396 | return append(b, seq_bytes_Currency[9]...), nil
397 | case BAM:
398 | return append(b, seq_bytes_Currency[10]...), nil
399 | case BBD:
400 | return append(b, seq_bytes_Currency[11]...), nil
401 | case BDT:
402 | return append(b, seq_bytes_Currency[12]...), nil
403 | case BGN:
404 | return append(b, seq_bytes_Currency[13]...), nil
405 | case BHD:
406 | return append(b, seq_bytes_Currency[14]...), nil
407 | case BIF:
408 | return append(b, seq_bytes_Currency[15]...), nil
409 | case BMD:
410 | return append(b, seq_bytes_Currency[16]...), nil
411 | case BND:
412 | return append(b, seq_bytes_Currency[17]...), nil
413 | case BOB:
414 | return append(b, seq_bytes_Currency[18]...), nil
415 | case BOV:
416 | return append(b, seq_bytes_Currency[19]...), nil
417 | case BRL:
418 | return append(b, seq_bytes_Currency[20]...), nil
419 | case BSD:
420 | return append(b, seq_bytes_Currency[21]...), nil
421 | case BTN:
422 | return append(b, seq_bytes_Currency[22]...), nil
423 | case BWP:
424 | return append(b, seq_bytes_Currency[23]...), nil
425 | case BYN:
426 | return append(b, seq_bytes_Currency[24]...), nil
427 | case BZD:
428 | return append(b, seq_bytes_Currency[25]...), nil
429 | case CAD:
430 | return append(b, seq_bytes_Currency[26]...), nil
431 | case CDF:
432 | return append(b, seq_bytes_Currency[27]...), nil
433 | case CHE:
434 | return append(b, seq_bytes_Currency[28]...), nil
435 | case CHF:
436 | return append(b, seq_bytes_Currency[29]...), nil
437 | case CHW:
438 | return append(b, seq_bytes_Currency[30]...), nil
439 | case CLP:
440 | return append(b, seq_bytes_Currency[31]...), nil
441 | case CNY:
442 | return append(b, seq_bytes_Currency[32]...), nil
443 | case COP:
444 | return append(b, seq_bytes_Currency[33]...), nil
445 | case COU:
446 | return append(b, seq_bytes_Currency[34]...), nil
447 | case CRC:
448 | return append(b, seq_bytes_Currency[35]...), nil
449 | case CUP:
450 | return append(b, seq_bytes_Currency[36]...), nil
451 | case CVE:
452 | return append(b, seq_bytes_Currency[37]...), nil
453 | case CZK:
454 | return append(b, seq_bytes_Currency[38]...), nil
455 | case DJF:
456 | return append(b, seq_bytes_Currency[39]...), nil
457 | case DKK:
458 | return append(b, seq_bytes_Currency[40]...), nil
459 | case DOP:
460 | return append(b, seq_bytes_Currency[41]...), nil
461 | case DZD:
462 | return append(b, seq_bytes_Currency[42]...), nil
463 | case EGP:
464 | return append(b, seq_bytes_Currency[43]...), nil
465 | case ERN:
466 | return append(b, seq_bytes_Currency[44]...), nil
467 | case ETB:
468 | return append(b, seq_bytes_Currency[45]...), nil
469 | case EUR:
470 | return append(b, seq_bytes_Currency[46]...), nil
471 | case FJD:
472 | return append(b, seq_bytes_Currency[47]...), nil
473 | case FKP:
474 | return append(b, seq_bytes_Currency[48]...), nil
475 | case GBP:
476 | return append(b, seq_bytes_Currency[49]...), nil
477 | case GEL:
478 | return append(b, seq_bytes_Currency[50]...), nil
479 | case GHS:
480 | return append(b, seq_bytes_Currency[51]...), nil
481 | case GIP:
482 | return append(b, seq_bytes_Currency[52]...), nil
483 | case GMD:
484 | return append(b, seq_bytes_Currency[53]...), nil
485 | case GNF:
486 | return append(b, seq_bytes_Currency[54]...), nil
487 | case GTQ:
488 | return append(b, seq_bytes_Currency[55]...), nil
489 | case GYD:
490 | return append(b, seq_bytes_Currency[56]...), nil
491 | case HKD:
492 | return append(b, seq_bytes_Currency[57]...), nil
493 | case HNL:
494 | return append(b, seq_bytes_Currency[58]...), nil
495 | case HRD:
496 | return append(b, seq_bytes_Currency[59]...), nil
497 | case HTG:
498 | return append(b, seq_bytes_Currency[60]...), nil
499 | case HUF:
500 | return append(b, seq_bytes_Currency[61]...), nil
501 | case IDR:
502 | return append(b, seq_bytes_Currency[62]...), nil
503 | case ILS:
504 | return append(b, seq_bytes_Currency[63]...), nil
505 | case INR:
506 | return append(b, seq_bytes_Currency[64]...), nil
507 | case IQD:
508 | return append(b, seq_bytes_Currency[65]...), nil
509 | case IRR:
510 | return append(b, seq_bytes_Currency[66]...), nil
511 | case ISK:
512 | return append(b, seq_bytes_Currency[67]...), nil
513 | case JMD:
514 | return append(b, seq_bytes_Currency[68]...), nil
515 | case JOD:
516 | return append(b, seq_bytes_Currency[69]...), nil
517 | case JPY:
518 | return append(b, seq_bytes_Currency[70]...), nil
519 | case KES:
520 | return append(b, seq_bytes_Currency[71]...), nil
521 | case KGS:
522 | return append(b, seq_bytes_Currency[72]...), nil
523 | case KHR:
524 | return append(b, seq_bytes_Currency[73]...), nil
525 | case KMF:
526 | return append(b, seq_bytes_Currency[74]...), nil
527 | case KPW:
528 | return append(b, seq_bytes_Currency[75]...), nil
529 | case KRW:
530 | return append(b, seq_bytes_Currency[76]...), nil
531 | case KWD:
532 | return append(b, seq_bytes_Currency[77]...), nil
533 | case KYD:
534 | return append(b, seq_bytes_Currency[78]...), nil
535 | case KZT:
536 | return append(b, seq_bytes_Currency[79]...), nil
537 | case LAK:
538 | return append(b, seq_bytes_Currency[80]...), nil
539 | case LBP:
540 | return append(b, seq_bytes_Currency[81]...), nil
541 | case LKR:
542 | return append(b, seq_bytes_Currency[82]...), nil
543 | case LRD:
544 | return append(b, seq_bytes_Currency[83]...), nil
545 | case LSL:
546 | return append(b, seq_bytes_Currency[84]...), nil
547 | case LYD:
548 | return append(b, seq_bytes_Currency[85]...), nil
549 | case MAD:
550 | return append(b, seq_bytes_Currency[86]...), nil
551 | case MDL:
552 | return append(b, seq_bytes_Currency[87]...), nil
553 | case MGA:
554 | return append(b, seq_bytes_Currency[88]...), nil
555 | case MKD:
556 | return append(b, seq_bytes_Currency[89]...), nil
557 | case MMK:
558 | return append(b, seq_bytes_Currency[90]...), nil
559 | case MNT:
560 | return append(b, seq_bytes_Currency[91]...), nil
561 | case MOP:
562 | return append(b, seq_bytes_Currency[92]...), nil
563 | case MRU:
564 | return append(b, seq_bytes_Currency[93]...), nil
565 | case MUR:
566 | return append(b, seq_bytes_Currency[94]...), nil
567 | case MVR:
568 | return append(b, seq_bytes_Currency[95]...), nil
569 | case MWK:
570 | return append(b, seq_bytes_Currency[96]...), nil
571 | case MXN:
572 | return append(b, seq_bytes_Currency[97]...), nil
573 | case MXV:
574 | return append(b, seq_bytes_Currency[98]...), nil
575 | case MYR:
576 | return append(b, seq_bytes_Currency[99]...), nil
577 | case MZN:
578 | return append(b, seq_bytes_Currency[100]...), nil
579 | case NAD:
580 | return append(b, seq_bytes_Currency[101]...), nil
581 | case NGN:
582 | return append(b, seq_bytes_Currency[102]...), nil
583 | case NIO:
584 | return append(b, seq_bytes_Currency[103]...), nil
585 | case NOK:
586 | return append(b, seq_bytes_Currency[104]...), nil
587 | case NPR:
588 | return append(b, seq_bytes_Currency[105]...), nil
589 | case NZD:
590 | return append(b, seq_bytes_Currency[106]...), nil
591 | case OMR:
592 | return append(b, seq_bytes_Currency[107]...), nil
593 | case PAB:
594 | return append(b, seq_bytes_Currency[108]...), nil
595 | case PEN:
596 | return append(b, seq_bytes_Currency[109]...), nil
597 | case PGK:
598 | return append(b, seq_bytes_Currency[110]...), nil
599 | case PHP:
600 | return append(b, seq_bytes_Currency[111]...), nil
601 | case PKR:
602 | return append(b, seq_bytes_Currency[112]...), nil
603 | case PLN:
604 | return append(b, seq_bytes_Currency[113]...), nil
605 | case PYG:
606 | return append(b, seq_bytes_Currency[114]...), nil
607 | case QAR:
608 | return append(b, seq_bytes_Currency[115]...), nil
609 | case RON:
610 | return append(b, seq_bytes_Currency[116]...), nil
611 | case RSD:
612 | return append(b, seq_bytes_Currency[117]...), nil
613 | case RUB:
614 | return append(b, seq_bytes_Currency[118]...), nil
615 | case RWF:
616 | return append(b, seq_bytes_Currency[119]...), nil
617 | case SAR:
618 | return append(b, seq_bytes_Currency[120]...), nil
619 | case SBD:
620 | return append(b, seq_bytes_Currency[121]...), nil
621 | case SCR:
622 | return append(b, seq_bytes_Currency[122]...), nil
623 | case SDG:
624 | return append(b, seq_bytes_Currency[123]...), nil
625 | case SEK:
626 | return append(b, seq_bytes_Currency[124]...), nil
627 | case SGD:
628 | return append(b, seq_bytes_Currency[125]...), nil
629 | case SHP:
630 | return append(b, seq_bytes_Currency[126]...), nil
631 | case SLE:
632 | return append(b, seq_bytes_Currency[127]...), nil
633 | case SOS:
634 | return append(b, seq_bytes_Currency[128]...), nil
635 | case SRD:
636 | return append(b, seq_bytes_Currency[129]...), nil
637 | case SSP:
638 | return append(b, seq_bytes_Currency[130]...), nil
639 | case STN:
640 | return append(b, seq_bytes_Currency[131]...), nil
641 | case SVC:
642 | return append(b, seq_bytes_Currency[132]...), nil
643 | case SYP:
644 | return append(b, seq_bytes_Currency[133]...), nil
645 | case SZL:
646 | return append(b, seq_bytes_Currency[134]...), nil
647 | case THB:
648 | return append(b, seq_bytes_Currency[135]...), nil
649 | case TJS:
650 | return append(b, seq_bytes_Currency[136]...), nil
651 | case TMT:
652 | return append(b, seq_bytes_Currency[137]...), nil
653 | case TND:
654 | return append(b, seq_bytes_Currency[138]...), nil
655 | case TOP:
656 | return append(b, seq_bytes_Currency[139]...), nil
657 | case TRY:
658 | return append(b, seq_bytes_Currency[140]...), nil
659 | case TTD:
660 | return append(b, seq_bytes_Currency[141]...), nil
661 | case TWD:
662 | return append(b, seq_bytes_Currency[142]...), nil
663 | case TZS:
664 | return append(b, seq_bytes_Currency[143]...), nil
665 | case UAH:
666 | return append(b, seq_bytes_Currency[144]...), nil
667 | case UGX:
668 | return append(b, seq_bytes_Currency[145]...), nil
669 | case USD:
670 | return append(b, seq_bytes_Currency[146]...), nil
671 | case USN:
672 | return append(b, seq_bytes_Currency[147]...), nil
673 | case UYI:
674 | return append(b, seq_bytes_Currency[148]...), nil
675 | case UYU:
676 | return append(b, seq_bytes_Currency[149]...), nil
677 | case UZS:
678 | return append(b, seq_bytes_Currency[150]...), nil
679 | case VED:
680 | return append(b, seq_bytes_Currency[151]...), nil
681 | case VES:
682 | return append(b, seq_bytes_Currency[152]...), nil
683 | case VND:
684 | return append(b, seq_bytes_Currency[153]...), nil
685 | case VUV:
686 | return append(b, seq_bytes_Currency[154]...), nil
687 | case WST:
688 | return append(b, seq_bytes_Currency[155]...), nil
689 | case XAF:
690 | return append(b, seq_bytes_Currency[156]...), nil
691 | case XAG:
692 | return append(b, seq_bytes_Currency[157]...), nil
693 | case XAU:
694 | return append(b, seq_bytes_Currency[158]...), nil
695 | case XBA:
696 | return append(b, seq_bytes_Currency[159]...), nil
697 | case XBB:
698 | return append(b, seq_bytes_Currency[160]...), nil
699 | case XBC:
700 | return append(b, seq_bytes_Currency[161]...), nil
701 | case XBD:
702 | return append(b, seq_bytes_Currency[162]...), nil
703 | case XCD:
704 | return append(b, seq_bytes_Currency[163]...), nil
705 | case XDR:
706 | return append(b, seq_bytes_Currency[164]...), nil
707 | case XOF:
708 | return append(b, seq_bytes_Currency[165]...), nil
709 | case XPD:
710 | return append(b, seq_bytes_Currency[166]...), nil
711 | case XPF:
712 | return append(b, seq_bytes_Currency[167]...), nil
713 | case XPT:
714 | return append(b, seq_bytes_Currency[168]...), nil
715 | case XSU:
716 | return append(b, seq_bytes_Currency[169]...), nil
717 | case XTS:
718 | return append(b, seq_bytes_Currency[170]...), nil
719 | case XUA:
720 | return append(b, seq_bytes_Currency[171]...), nil
721 | case XXX:
722 | return append(b, seq_bytes_Currency[172]...), nil
723 | case YER:
724 | return append(b, seq_bytes_Currency[173]...), nil
725 | case ZAR:
726 | return append(b, seq_bytes_Currency[174]...), nil
727 | case ZMW:
728 | return append(b, seq_bytes_Currency[175]...), nil
729 | case ZWG:
730 | return append(b, seq_bytes_Currency[176]...), nil
731 | default:
732 | return nil, ErrUnknownCurrency
733 | }
734 | }
735 |
736 | var seq_string_Currency = [...]string{"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD", "CAD", "CDF", "CHE", "CHF", "CHW", "CLP", "CNY", "COP", "COU", "CRC", "CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRD", "HTG", "HUF", "IDR", "ILS", "INR", "IQD", "IRR", "ISK", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRU", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLE", "SOS", "SRD", "SSP", "STN", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "USN", "UYI", "UYU", "UZS", "VED", "VES", "VND", "VUV", "WST", "XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX", "YER", "ZAR", "ZMW", "ZWG"}
737 |
738 | func (s Currency) String() string {
739 | switch s {
740 | case AED:
741 | return seq_string_Currency[0]
742 | case AFN:
743 | return seq_string_Currency[1]
744 | case ALL:
745 | return seq_string_Currency[2]
746 | case AMD:
747 | return seq_string_Currency[3]
748 | case ANG:
749 | return seq_string_Currency[4]
750 | case AOA:
751 | return seq_string_Currency[5]
752 | case ARS:
753 | return seq_string_Currency[6]
754 | case AUD:
755 | return seq_string_Currency[7]
756 | case AWG:
757 | return seq_string_Currency[8]
758 | case AZN:
759 | return seq_string_Currency[9]
760 | case BAM:
761 | return seq_string_Currency[10]
762 | case BBD:
763 | return seq_string_Currency[11]
764 | case BDT:
765 | return seq_string_Currency[12]
766 | case BGN:
767 | return seq_string_Currency[13]
768 | case BHD:
769 | return seq_string_Currency[14]
770 | case BIF:
771 | return seq_string_Currency[15]
772 | case BMD:
773 | return seq_string_Currency[16]
774 | case BND:
775 | return seq_string_Currency[17]
776 | case BOB:
777 | return seq_string_Currency[18]
778 | case BOV:
779 | return seq_string_Currency[19]
780 | case BRL:
781 | return seq_string_Currency[20]
782 | case BSD:
783 | return seq_string_Currency[21]
784 | case BTN:
785 | return seq_string_Currency[22]
786 | case BWP:
787 | return seq_string_Currency[23]
788 | case BYN:
789 | return seq_string_Currency[24]
790 | case BZD:
791 | return seq_string_Currency[25]
792 | case CAD:
793 | return seq_string_Currency[26]
794 | case CDF:
795 | return seq_string_Currency[27]
796 | case CHE:
797 | return seq_string_Currency[28]
798 | case CHF:
799 | return seq_string_Currency[29]
800 | case CHW:
801 | return seq_string_Currency[30]
802 | case CLP:
803 | return seq_string_Currency[31]
804 | case CNY:
805 | return seq_string_Currency[32]
806 | case COP:
807 | return seq_string_Currency[33]
808 | case COU:
809 | return seq_string_Currency[34]
810 | case CRC:
811 | return seq_string_Currency[35]
812 | case CUP:
813 | return seq_string_Currency[36]
814 | case CVE:
815 | return seq_string_Currency[37]
816 | case CZK:
817 | return seq_string_Currency[38]
818 | case DJF:
819 | return seq_string_Currency[39]
820 | case DKK:
821 | return seq_string_Currency[40]
822 | case DOP:
823 | return seq_string_Currency[41]
824 | case DZD:
825 | return seq_string_Currency[42]
826 | case EGP:
827 | return seq_string_Currency[43]
828 | case ERN:
829 | return seq_string_Currency[44]
830 | case ETB:
831 | return seq_string_Currency[45]
832 | case EUR:
833 | return seq_string_Currency[46]
834 | case FJD:
835 | return seq_string_Currency[47]
836 | case FKP:
837 | return seq_string_Currency[48]
838 | case GBP:
839 | return seq_string_Currency[49]
840 | case GEL:
841 | return seq_string_Currency[50]
842 | case GHS:
843 | return seq_string_Currency[51]
844 | case GIP:
845 | return seq_string_Currency[52]
846 | case GMD:
847 | return seq_string_Currency[53]
848 | case GNF:
849 | return seq_string_Currency[54]
850 | case GTQ:
851 | return seq_string_Currency[55]
852 | case GYD:
853 | return seq_string_Currency[56]
854 | case HKD:
855 | return seq_string_Currency[57]
856 | case HNL:
857 | return seq_string_Currency[58]
858 | case HRD:
859 | return seq_string_Currency[59]
860 | case HTG:
861 | return seq_string_Currency[60]
862 | case HUF:
863 | return seq_string_Currency[61]
864 | case IDR:
865 | return seq_string_Currency[62]
866 | case ILS:
867 | return seq_string_Currency[63]
868 | case INR:
869 | return seq_string_Currency[64]
870 | case IQD:
871 | return seq_string_Currency[65]
872 | case IRR:
873 | return seq_string_Currency[66]
874 | case ISK:
875 | return seq_string_Currency[67]
876 | case JMD:
877 | return seq_string_Currency[68]
878 | case JOD:
879 | return seq_string_Currency[69]
880 | case JPY:
881 | return seq_string_Currency[70]
882 | case KES:
883 | return seq_string_Currency[71]
884 | case KGS:
885 | return seq_string_Currency[72]
886 | case KHR:
887 | return seq_string_Currency[73]
888 | case KMF:
889 | return seq_string_Currency[74]
890 | case KPW:
891 | return seq_string_Currency[75]
892 | case KRW:
893 | return seq_string_Currency[76]
894 | case KWD:
895 | return seq_string_Currency[77]
896 | case KYD:
897 | return seq_string_Currency[78]
898 | case KZT:
899 | return seq_string_Currency[79]
900 | case LAK:
901 | return seq_string_Currency[80]
902 | case LBP:
903 | return seq_string_Currency[81]
904 | case LKR:
905 | return seq_string_Currency[82]
906 | case LRD:
907 | return seq_string_Currency[83]
908 | case LSL:
909 | return seq_string_Currency[84]
910 | case LYD:
911 | return seq_string_Currency[85]
912 | case MAD:
913 | return seq_string_Currency[86]
914 | case MDL:
915 | return seq_string_Currency[87]
916 | case MGA:
917 | return seq_string_Currency[88]
918 | case MKD:
919 | return seq_string_Currency[89]
920 | case MMK:
921 | return seq_string_Currency[90]
922 | case MNT:
923 | return seq_string_Currency[91]
924 | case MOP:
925 | return seq_string_Currency[92]
926 | case MRU:
927 | return seq_string_Currency[93]
928 | case MUR:
929 | return seq_string_Currency[94]
930 | case MVR:
931 | return seq_string_Currency[95]
932 | case MWK:
933 | return seq_string_Currency[96]
934 | case MXN:
935 | return seq_string_Currency[97]
936 | case MXV:
937 | return seq_string_Currency[98]
938 | case MYR:
939 | return seq_string_Currency[99]
940 | case MZN:
941 | return seq_string_Currency[100]
942 | case NAD:
943 | return seq_string_Currency[101]
944 | case NGN:
945 | return seq_string_Currency[102]
946 | case NIO:
947 | return seq_string_Currency[103]
948 | case NOK:
949 | return seq_string_Currency[104]
950 | case NPR:
951 | return seq_string_Currency[105]
952 | case NZD:
953 | return seq_string_Currency[106]
954 | case OMR:
955 | return seq_string_Currency[107]
956 | case PAB:
957 | return seq_string_Currency[108]
958 | case PEN:
959 | return seq_string_Currency[109]
960 | case PGK:
961 | return seq_string_Currency[110]
962 | case PHP:
963 | return seq_string_Currency[111]
964 | case PKR:
965 | return seq_string_Currency[112]
966 | case PLN:
967 | return seq_string_Currency[113]
968 | case PYG:
969 | return seq_string_Currency[114]
970 | case QAR:
971 | return seq_string_Currency[115]
972 | case RON:
973 | return seq_string_Currency[116]
974 | case RSD:
975 | return seq_string_Currency[117]
976 | case RUB:
977 | return seq_string_Currency[118]
978 | case RWF:
979 | return seq_string_Currency[119]
980 | case SAR:
981 | return seq_string_Currency[120]
982 | case SBD:
983 | return seq_string_Currency[121]
984 | case SCR:
985 | return seq_string_Currency[122]
986 | case SDG:
987 | return seq_string_Currency[123]
988 | case SEK:
989 | return seq_string_Currency[124]
990 | case SGD:
991 | return seq_string_Currency[125]
992 | case SHP:
993 | return seq_string_Currency[126]
994 | case SLE:
995 | return seq_string_Currency[127]
996 | case SOS:
997 | return seq_string_Currency[128]
998 | case SRD:
999 | return seq_string_Currency[129]
1000 | case SSP:
1001 | return seq_string_Currency[130]
1002 | case STN:
1003 | return seq_string_Currency[131]
1004 | case SVC:
1005 | return seq_string_Currency[132]
1006 | case SYP:
1007 | return seq_string_Currency[133]
1008 | case SZL:
1009 | return seq_string_Currency[134]
1010 | case THB:
1011 | return seq_string_Currency[135]
1012 | case TJS:
1013 | return seq_string_Currency[136]
1014 | case TMT:
1015 | return seq_string_Currency[137]
1016 | case TND:
1017 | return seq_string_Currency[138]
1018 | case TOP:
1019 | return seq_string_Currency[139]
1020 | case TRY:
1021 | return seq_string_Currency[140]
1022 | case TTD:
1023 | return seq_string_Currency[141]
1024 | case TWD:
1025 | return seq_string_Currency[142]
1026 | case TZS:
1027 | return seq_string_Currency[143]
1028 | case UAH:
1029 | return seq_string_Currency[144]
1030 | case UGX:
1031 | return seq_string_Currency[145]
1032 | case USD:
1033 | return seq_string_Currency[146]
1034 | case USN:
1035 | return seq_string_Currency[147]
1036 | case UYI:
1037 | return seq_string_Currency[148]
1038 | case UYU:
1039 | return seq_string_Currency[149]
1040 | case UZS:
1041 | return seq_string_Currency[150]
1042 | case VED:
1043 | return seq_string_Currency[151]
1044 | case VES:
1045 | return seq_string_Currency[152]
1046 | case VND:
1047 | return seq_string_Currency[153]
1048 | case VUV:
1049 | return seq_string_Currency[154]
1050 | case WST:
1051 | return seq_string_Currency[155]
1052 | case XAF:
1053 | return seq_string_Currency[156]
1054 | case XAG:
1055 | return seq_string_Currency[157]
1056 | case XAU:
1057 | return seq_string_Currency[158]
1058 | case XBA:
1059 | return seq_string_Currency[159]
1060 | case XBB:
1061 | return seq_string_Currency[160]
1062 | case XBC:
1063 | return seq_string_Currency[161]
1064 | case XBD:
1065 | return seq_string_Currency[162]
1066 | case XCD:
1067 | return seq_string_Currency[163]
1068 | case XDR:
1069 | return seq_string_Currency[164]
1070 | case XOF:
1071 | return seq_string_Currency[165]
1072 | case XPD:
1073 | return seq_string_Currency[166]
1074 | case XPF:
1075 | return seq_string_Currency[167]
1076 | case XPT:
1077 | return seq_string_Currency[168]
1078 | case XSU:
1079 | return seq_string_Currency[169]
1080 | case XTS:
1081 | return seq_string_Currency[170]
1082 | case XUA:
1083 | return seq_string_Currency[171]
1084 | case XXX:
1085 | return seq_string_Currency[172]
1086 | case YER:
1087 | return seq_string_Currency[173]
1088 | case ZAR:
1089 | return seq_string_Currency[174]
1090 | case ZMW:
1091 | return seq_string_Currency[175]
1092 | case ZWG:
1093 | return seq_string_Currency[176]
1094 | default:
1095 | return ""
1096 | }
1097 | }
1098 |
--------------------------------------------------------------------------------
/currency_enum_encoding_test.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-enum-encoding; DO NOT EDIT.
2 |
3 | package fpmoney
4 |
5 | import (
6 | "encoding/json"
7 | "errors"
8 | "fmt"
9 | "math/rand"
10 | "slices"
11 | "testing"
12 | )
13 |
14 | func ExampleCurrency_MarshalText() {
15 | for _, v := range []Currency{AED, AFN, ALL, AMD, ANG, AOA, ARS, AUD, AWG, AZN, BAM, BBD, BDT, BGN, BHD, BIF, BMD, BND, BOB, BOV, BRL, BSD, BTN, BWP, BYN, BZD, CAD, CDF, CHE, CHF, CHW, CLP, CNY, COP, COU, CRC, CUP, CVE, CZK, DJF, DKK, DOP, DZD, EGP, ERN, ETB, EUR, FJD, FKP, GBP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, HKD, HNL, HRD, HTG, HUF, IDR, ILS, INR, IQD, IRR, ISK, JMD, JOD, JPY, KES, KGS, KHR, KMF, KPW, KRW, KWD, KYD, KZT, LAK, LBP, LKR, LRD, LSL, LYD, MAD, MDL, MGA, MKD, MMK, MNT, MOP, MRU, MUR, MVR, MWK, MXN, MXV, MYR, MZN, NAD, NGN, NIO, NOK, NPR, NZD, OMR, PAB, PEN, PGK, PHP, PKR, PLN, PYG, QAR, RON, RSD, RUB, RWF, SAR, SBD, SCR, SDG, SEK, SGD, SHP, SLE, SOS, SRD, SSP, STN, SVC, SYP, SZL, THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, UAH, UGX, USD, USN, UYI, UYU, UZS, VED, VES, VND, VUV, WST, XAF, XAG, XAU, XBA, XBB, XBC, XBD, XCD, XDR, XOF, XPD, XPF, XPT, XSU, XTS, XUA, XXX, YER, ZAR, ZMW, ZWG} {
16 | b, _ := v.MarshalText()
17 | fmt.Printf("%s ", string(b))
18 | }
19 | // Output: AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF BMD BND BOB BOV BRL BSD BTN BWP BYN BZD CAD CDF CHE CHF CHW CLP CNY COP COU CRC CUP CVE CZK DJF DKK DOP DZD EGP ERN ETB EUR FJD FKP GBP GEL GHS GIP GMD GNF GTQ GYD HKD HNL HRD HTG HUF IDR ILS INR IQD IRR ISK JMD JOD JPY KES KGS KHR KMF KPW KRW KWD KYD KZT LAK LBP LKR LRD LSL LYD MAD MDL MGA MKD MMK MNT MOP MRU MUR MVR MWK MXN MXV MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SHP SLE SOS SRD SSP STN SVC SYP SZL THB TJS TMT TND TOP TRY TTD TWD TZS UAH UGX USD USN UYI UYU UZS VED VES VND VUV WST XAF XAG XAU XBA XBB XBC XBD XCD XDR XOF XPD XPF XPT XSU XTS XUA XXX YER ZAR ZMW ZWG
20 | }
21 |
22 | func ExampleCurrency_UnmarshalText() {
23 | for _, s := range []string{"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD", "CAD", "CDF", "CHE", "CHF", "CHW", "CLP", "CNY", "COP", "COU", "CRC", "CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRD", "HTG", "HUF", "IDR", "ILS", "INR", "IQD", "IRR", "ISK", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRU", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLE", "SOS", "SRD", "SSP", "STN", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "USN", "UYI", "UYU", "UZS", "VED", "VES", "VND", "VUV", "WST", "XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX", "YER", "ZAR", "ZMW", "ZWG"} {
24 | var v Currency
25 | if err := (&v).UnmarshalText([]byte(s)); err != nil {
26 | fmt.Println(err)
27 | }
28 | }
29 | }
30 |
31 | func TestCurrency_MarshalText_UnmarshalText(t *testing.T) {
32 | for _, v := range []Currency{AED, AFN, ALL, AMD, ANG, AOA, ARS, AUD, AWG, AZN, BAM, BBD, BDT, BGN, BHD, BIF, BMD, BND, BOB, BOV, BRL, BSD, BTN, BWP, BYN, BZD, CAD, CDF, CHE, CHF, CHW, CLP, CNY, COP, COU, CRC, CUP, CVE, CZK, DJF, DKK, DOP, DZD, EGP, ERN, ETB, EUR, FJD, FKP, GBP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, HKD, HNL, HRD, HTG, HUF, IDR, ILS, INR, IQD, IRR, ISK, JMD, JOD, JPY, KES, KGS, KHR, KMF, KPW, KRW, KWD, KYD, KZT, LAK, LBP, LKR, LRD, LSL, LYD, MAD, MDL, MGA, MKD, MMK, MNT, MOP, MRU, MUR, MVR, MWK, MXN, MXV, MYR, MZN, NAD, NGN, NIO, NOK, NPR, NZD, OMR, PAB, PEN, PGK, PHP, PKR, PLN, PYG, QAR, RON, RSD, RUB, RWF, SAR, SBD, SCR, SDG, SEK, SGD, SHP, SLE, SOS, SRD, SSP, STN, SVC, SYP, SZL, THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, UAH, UGX, USD, USN, UYI, UYU, UZS, VED, VES, VND, VUV, WST, XAF, XAG, XAU, XBA, XBB, XBC, XBD, XCD, XDR, XOF, XPD, XPF, XPT, XSU, XTS, XUA, XXX, YER, ZAR, ZMW, ZWG} {
33 | b, err := v.MarshalText()
34 | if err != nil {
35 | t.Errorf("cannot encode: %s", err)
36 | }
37 |
38 | var d Currency
39 | if err := (&d).UnmarshalText(b); err != nil {
40 | t.Errorf("cannot decode: %s", err)
41 | }
42 |
43 | if d != v {
44 | t.Errorf("exp(%v) != got(%v)", v, d)
45 | }
46 | }
47 |
48 | t.Run("when unknown value, then error", func(t *testing.T) {
49 | s := `something`
50 | var v Currency
51 | err := (&v).UnmarshalText([]byte(s))
52 | if err == nil {
53 | t.Error("must be error")
54 | }
55 | if !errors.Is(err, ErrUnknownCurrency) {
56 | t.Error("wrong error", err)
57 | }
58 | })
59 | }
60 |
61 | func TestCurrency_JSON(t *testing.T) {
62 | type V struct {
63 | Values []Currency `json:"values"`
64 | }
65 |
66 | values := []Currency{AED, AFN, ALL, AMD, ANG, AOA, ARS, AUD, AWG, AZN, BAM, BBD, BDT, BGN, BHD, BIF, BMD, BND, BOB, BOV, BRL, BSD, BTN, BWP, BYN, BZD, CAD, CDF, CHE, CHF, CHW, CLP, CNY, COP, COU, CRC, CUP, CVE, CZK, DJF, DKK, DOP, DZD, EGP, ERN, ETB, EUR, FJD, FKP, GBP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, HKD, HNL, HRD, HTG, HUF, IDR, ILS, INR, IQD, IRR, ISK, JMD, JOD, JPY, KES, KGS, KHR, KMF, KPW, KRW, KWD, KYD, KZT, LAK, LBP, LKR, LRD, LSL, LYD, MAD, MDL, MGA, MKD, MMK, MNT, MOP, MRU, MUR, MVR, MWK, MXN, MXV, MYR, MZN, NAD, NGN, NIO, NOK, NPR, NZD, OMR, PAB, PEN, PGK, PHP, PKR, PLN, PYG, QAR, RON, RSD, RUB, RWF, SAR, SBD, SCR, SDG, SEK, SGD, SHP, SLE, SOS, SRD, SSP, STN, SVC, SYP, SZL, THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, UAH, UGX, USD, USN, UYI, UYU, UZS, VED, VES, VND, VUV, WST, XAF, XAG, XAU, XBA, XBB, XBC, XBD, XCD, XDR, XOF, XPD, XPF, XPT, XSU, XTS, XUA, XXX, YER, ZAR, ZMW, ZWG}
67 |
68 | var v V
69 | s := `{"values":["AED","AFN","ALL","AMD","ANG","AOA","ARS","AUD","AWG","AZN","BAM","BBD","BDT","BGN","BHD","BIF","BMD","BND","BOB","BOV","BRL","BSD","BTN","BWP","BYN","BZD","CAD","CDF","CHE","CHF","CHW","CLP","CNY","COP","COU","CRC","CUP","CVE","CZK","DJF","DKK","DOP","DZD","EGP","ERN","ETB","EUR","FJD","FKP","GBP","GEL","GHS","GIP","GMD","GNF","GTQ","GYD","HKD","HNL","HRD","HTG","HUF","IDR","ILS","INR","IQD","IRR","ISK","JMD","JOD","JPY","KES","KGS","KHR","KMF","KPW","KRW","KWD","KYD","KZT","LAK","LBP","LKR","LRD","LSL","LYD","MAD","MDL","MGA","MKD","MMK","MNT","MOP","MRU","MUR","MVR","MWK","MXN","MXV","MYR","MZN","NAD","NGN","NIO","NOK","NPR","NZD","OMR","PAB","PEN","PGK","PHP","PKR","PLN","PYG","QAR","RON","RSD","RUB","RWF","SAR","SBD","SCR","SDG","SEK","SGD","SHP","SLE","SOS","SRD","SSP","STN","SVC","SYP","SZL","THB","TJS","TMT","TND","TOP","TRY","TTD","TWD","TZS","UAH","UGX","USD","USN","UYI","UYU","UZS","VED","VES","VND","VUV","WST","XAF","XAG","XAU","XBA","XBB","XBC","XBD","XCD","XDR","XOF","XPD","XPF","XPT","XSU","XTS","XUA","XXX","YER","ZAR","ZMW","ZWG"]}`
70 | json.Unmarshal([]byte(s), &v)
71 |
72 | if len(v.Values) != len(values) {
73 | t.Errorf("cannot decode: %d", len(v.Values))
74 | }
75 | if !slices.Equal(v.Values, values) {
76 | t.Errorf("wrong decoded: %v", v.Values)
77 | }
78 |
79 | b, err := json.Marshal(v)
80 | if err != nil {
81 | t.Fatalf("cannot encode: %s", err)
82 | }
83 | if string(b) != s {
84 | t.Errorf("wrong encoded: %s != %s", string(b), s)
85 | }
86 |
87 | t.Run("when unknown value, then error", func(t *testing.T) {
88 | s := `{"values":["something"]}`
89 | var v V
90 | err := json.Unmarshal([]byte(s), &v)
91 | if err == nil {
92 | t.Error("must be error")
93 | }
94 | if !errors.Is(err, ErrUnknownCurrency) {
95 | t.Error("wrong error", err)
96 | }
97 | })
98 | }
99 |
100 | func BenchmarkCurrency_UnmarshalText(b *testing.B) {
101 | vb := seq_bytes_Currency[rand.Intn(len(seq_bytes_Currency))]
102 |
103 | var x Currency
104 |
105 | for b.Loop() {
106 | _ = x.UnmarshalText(vb)
107 | }
108 | }
109 |
110 | func BenchmarkCurrency_AppendText(b *testing.B) {
111 | bb := make([]byte, 10, 1000)
112 |
113 | vs := []Currency{AED, AFN, ALL, AMD, ANG, AOA, ARS, AUD, AWG, AZN, BAM, BBD, BDT, BGN, BHD, BIF, BMD, BND, BOB, BOV, BRL, BSD, BTN, BWP, BYN, BZD, CAD, CDF, CHE, CHF, CHW, CLP, CNY, COP, COU, CRC, CUP, CVE, CZK, DJF, DKK, DOP, DZD, EGP, ERN, ETB, EUR, FJD, FKP, GBP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, HKD, HNL, HRD, HTG, HUF, IDR, ILS, INR, IQD, IRR, ISK, JMD, JOD, JPY, KES, KGS, KHR, KMF, KPW, KRW, KWD, KYD, KZT, LAK, LBP, LKR, LRD, LSL, LYD, MAD, MDL, MGA, MKD, MMK, MNT, MOP, MRU, MUR, MVR, MWK, MXN, MXV, MYR, MZN, NAD, NGN, NIO, NOK, NPR, NZD, OMR, PAB, PEN, PGK, PHP, PKR, PLN, PYG, QAR, RON, RSD, RUB, RWF, SAR, SBD, SCR, SDG, SEK, SGD, SHP, SLE, SOS, SRD, SSP, STN, SVC, SYP, SZL, THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, UAH, UGX, USD, USN, UYI, UYU, UZS, VED, VES, VND, VUV, WST, XAF, XAG, XAU, XBA, XBB, XBC, XBD, XCD, XDR, XOF, XPD, XPF, XPT, XSU, XTS, XUA, XXX, YER, ZAR, ZMW, ZWG}
114 | v := vs[rand.Intn(len(vs))]
115 |
116 | for b.Loop() {
117 | _, _ = v.AppendText(bb)
118 | }
119 | }
120 |
121 | func BenchmarkCurrency_MarshalText(b *testing.B) {
122 | vs := []Currency{AED, AFN, ALL, AMD, ANG, AOA, ARS, AUD, AWG, AZN, BAM, BBD, BDT, BGN, BHD, BIF, BMD, BND, BOB, BOV, BRL, BSD, BTN, BWP, BYN, BZD, CAD, CDF, CHE, CHF, CHW, CLP, CNY, COP, COU, CRC, CUP, CVE, CZK, DJF, DKK, DOP, DZD, EGP, ERN, ETB, EUR, FJD, FKP, GBP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, HKD, HNL, HRD, HTG, HUF, IDR, ILS, INR, IQD, IRR, ISK, JMD, JOD, JPY, KES, KGS, KHR, KMF, KPW, KRW, KWD, KYD, KZT, LAK, LBP, LKR, LRD, LSL, LYD, MAD, MDL, MGA, MKD, MMK, MNT, MOP, MRU, MUR, MVR, MWK, MXN, MXV, MYR, MZN, NAD, NGN, NIO, NOK, NPR, NZD, OMR, PAB, PEN, PGK, PHP, PKR, PLN, PYG, QAR, RON, RSD, RUB, RWF, SAR, SBD, SCR, SDG, SEK, SGD, SHP, SLE, SOS, SRD, SSP, STN, SVC, SYP, SZL, THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, UAH, UGX, USD, USN, UYI, UYU, UZS, VED, VES, VND, VUV, WST, XAF, XAG, XAU, XBA, XBB, XBC, XBD, XCD, XDR, XOF, XPD, XPF, XPT, XSU, XTS, XUA, XXX, YER, ZAR, ZMW, ZWG}
123 | v := vs[rand.Intn(len(vs))]
124 |
125 | for b.Loop() {
126 | _, _ = v.MarshalText()
127 | }
128 | }
129 |
130 | func TestCurrency_String(t *testing.T) {
131 | values := []Currency{AED, AFN, ALL, AMD, ANG, AOA, ARS, AUD, AWG, AZN, BAM, BBD, BDT, BGN, BHD, BIF, BMD, BND, BOB, BOV, BRL, BSD, BTN, BWP, BYN, BZD, CAD, CDF, CHE, CHF, CHW, CLP, CNY, COP, COU, CRC, CUP, CVE, CZK, DJF, DKK, DOP, DZD, EGP, ERN, ETB, EUR, FJD, FKP, GBP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, HKD, HNL, HRD, HTG, HUF, IDR, ILS, INR, IQD, IRR, ISK, JMD, JOD, JPY, KES, KGS, KHR, KMF, KPW, KRW, KWD, KYD, KZT, LAK, LBP, LKR, LRD, LSL, LYD, MAD, MDL, MGA, MKD, MMK, MNT, MOP, MRU, MUR, MVR, MWK, MXN, MXV, MYR, MZN, NAD, NGN, NIO, NOK, NPR, NZD, OMR, PAB, PEN, PGK, PHP, PKR, PLN, PYG, QAR, RON, RSD, RUB, RWF, SAR, SBD, SCR, SDG, SEK, SGD, SHP, SLE, SOS, SRD, SSP, STN, SVC, SYP, SZL, THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, UAH, UGX, USD, USN, UYI, UYU, UZS, VED, VES, VND, VUV, WST, XAF, XAG, XAU, XBA, XBB, XBC, XBD, XCD, XDR, XOF, XPD, XPF, XPT, XSU, XTS, XUA, XXX, YER, ZAR, ZMW, ZWG}
132 | tags := []string{"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD", "CAD", "CDF", "CHE", "CHF", "CHW", "CLP", "CNY", "COP", "COU", "CRC", "CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRD", "HTG", "HUF", "IDR", "ILS", "INR", "IQD", "IRR", "ISK", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRU", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLE", "SOS", "SRD", "SSP", "STN", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "USN", "UYI", "UYU", "UZS", "VED", "VES", "VND", "VUV", "WST", "XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX", "YER", "ZAR", "ZMW", "ZWG"}
133 |
134 | for i := range values {
135 | if s := values[i].String(); s != tags[i] {
136 | t.Error(s, tags[i])
137 | }
138 | }
139 | }
140 |
141 | func BenchmarkCurrency_String(b *testing.B) {
142 | vs := []Currency{AED, AFN, ALL, AMD, ANG, AOA, ARS, AUD, AWG, AZN, BAM, BBD, BDT, BGN, BHD, BIF, BMD, BND, BOB, BOV, BRL, BSD, BTN, BWP, BYN, BZD, CAD, CDF, CHE, CHF, CHW, CLP, CNY, COP, COU, CRC, CUP, CVE, CZK, DJF, DKK, DOP, DZD, EGP, ERN, ETB, EUR, FJD, FKP, GBP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, HKD, HNL, HRD, HTG, HUF, IDR, ILS, INR, IQD, IRR, ISK, JMD, JOD, JPY, KES, KGS, KHR, KMF, KPW, KRW, KWD, KYD, KZT, LAK, LBP, LKR, LRD, LSL, LYD, MAD, MDL, MGA, MKD, MMK, MNT, MOP, MRU, MUR, MVR, MWK, MXN, MXV, MYR, MZN, NAD, NGN, NIO, NOK, NPR, NZD, OMR, PAB, PEN, PGK, PHP, PKR, PLN, PYG, QAR, RON, RSD, RUB, RWF, SAR, SBD, SCR, SDG, SEK, SGD, SHP, SLE, SOS, SRD, SSP, STN, SVC, SYP, SZL, THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, UAH, UGX, USD, USN, UYI, UYU, UZS, VED, VES, VND, VUV, WST, XAF, XAG, XAU, XBA, XBB, XBC, XBD, XCD, XDR, XOF, XPD, XPF, XPT, XSU, XTS, XUA, XXX, YER, ZAR, ZMW, ZWG}
143 | v := vs[rand.Intn(len(vs))]
144 |
145 | for b.Loop() {
146 | _ = v.String()
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/nikolaydubina/fpmoney
2 |
3 | go 1.24
4 |
5 | require github.com/nikolaydubina/fpdecimal v0.20.2
6 |
7 | retract v1.1.0 // Adds fractional minor units
8 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/nikolaydubina/fpdecimal v0.20.2 h1:geV6ODnGCy4CoiaWHmqjwD94PboF24rSOnp6uisPAfg=
2 | github.com/nikolaydubina/fpdecimal v0.20.2/go.mod h1:FtuJCNRO3SumT8lfJNp5IZ8kcnPzx7blxiUe3VcTTsY=
3 |
--------------------------------------------------------------------------------
/testdata/amount-float-large.jsonl:
--------------------------------------------------------------------------------
1 | {"currency": "KRW", "amount": 123123123112312.1232}
2 | {"currency": "KRW", "amount": 5341320482340234.123}
--------------------------------------------------------------------------------
/testdata/amount-float-small.jsonl:
--------------------------------------------------------------------------------
1 | {"currency": "KRW", "amount": 123.456}
2 | {"currency": "KRW", "amount": 0.123}
3 | {"currency": "KRW", "amount": 0.012}
4 | {"currency": "KRW", "amount": 0.001}
5 | {"currency": "KRW", "amount": 0.982}
6 | {"currency": "KRW", "amount": 0.101}
7 | {"currency": "KRW", "amount": 10}
8 | {"currency": "KRW", "amount": 11}
9 | {"currency": "KRW", "amount": 1}
--------------------------------------------------------------------------------