├── .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 | [![codecov](https://codecov.io/gh/nikolaydubina/fpmoney/branch/master/graph/badge.svg?token=Eh52jhLERp)](https://codecov.io/gh/nikolaydubina/fpmoney) 8 | [![Go Report Card](https://goreportcard.com/badge/github.com/nikolaydubina/fpmoney)](https://goreportcard.com/report/github.com/nikolaydubina/fpmoney) 9 | [![Go Reference](https://pkg.go.dev/badge/github.com/nikolaydubina/fpmoney.svg)](https://pkg.go.dev/github.com/nikolaydubina/fpmoney) 10 | [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go) 11 | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/nikolaydubina/fpmoney/badge)](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} --------------------------------------------------------------------------------