summaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/text/currency/currency.go
blob: 598ddeff420b058eb0ba041945e1646fa6a610e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:generate go run gen.go gen_common.go -output tables.go

// Package currency contains currency-related functionality.
//
// NOTE: the formatting functionality is currently under development and may
// change without notice.
package currency // import "golang.org/x/text/currency"

import (
	"errors"
	"sort"

	"golang.org/x/text/internal/tag"
	"golang.org/x/text/language"
)

// TODO:
// - language-specific currency names.
// - currency formatting.
// - currency information per region
// - register currency code (there are no private use area)

// TODO: remove Currency type from package language.

// Kind determines the rounding and rendering properties of a currency value.
type Kind struct {
	rounding rounding
	// TODO: formatting type: standard, accounting. See CLDR.
}

type rounding byte

const (
	standard rounding = iota
	cash
)

var (
	// Standard defines standard rounding and formatting for currencies.
	Standard Kind = Kind{rounding: standard}

	// Cash defines rounding and formatting standards for cash transactions.
	Cash Kind = Kind{rounding: cash}

	// Accounting defines rounding and formatting standards for accounting.
	Accounting Kind = Kind{rounding: standard}
)

// Rounding reports the rounding characteristics for the given currency, where
// scale is the number of fractional decimals and increment is the number of
// units in terms of 10^(-scale) to which to round to.
func (k Kind) Rounding(cur Unit) (scale, increment int) {
	info := currency.Elem(int(cur.index))[3]
	switch k.rounding {
	case standard:
		info &= roundMask
	case cash:
		info >>= cashShift
	}
	return int(roundings[info].scale), int(roundings[info].increment)
}

// Unit is an ISO 4217 currency designator.
type Unit struct {
	index uint16
}

// String returns the ISO code of u.
func (u Unit) String() string {
	if u.index == 0 {
		return "XXX"
	}
	return currency.Elem(int(u.index))[:3]
}

// Amount creates an Amount for the given currency unit and amount.
func (u Unit) Amount(amount interface{}) Amount {
	// TODO: verify amount is a supported number type
	return Amount{amount: amount, currency: u}
}

var (
	errSyntax = errors.New("currency: tag is not well-formed")
	errValue  = errors.New("currency: tag is not a recognized currency")
)

// ParseISO parses a 3-letter ISO 4217 currency code. It returns an error if s
// is not well-formed or not a recognized currency code.
func ParseISO(s string) (Unit, error) {
	var buf [4]byte // Take one byte more to detect oversize keys.
	key := buf[:copy(buf[:], s)]
	if !tag.FixCase("XXX", key) {
		return Unit{}, errSyntax
	}
	if i := currency.Index(key); i >= 0 {
		if i == xxx {
			return Unit{}, nil
		}
		return Unit{uint16(i)}, nil
	}
	return Unit{}, errValue
}

// MustParseISO is like ParseISO, but panics if the given currency unit
// cannot be parsed. It simplifies safe initialization of Unit values.
func MustParseISO(s string) Unit {
	c, err := ParseISO(s)
	if err != nil {
		panic(err)
	}
	return c
}

// FromRegion reports the currency unit that is currently legal tender in the
// given region according to CLDR. It will return false if region currently does
// not have a legal tender.
func FromRegion(r language.Region) (currency Unit, ok bool) {
	x := regionToCode(r)
	i := sort.Search(len(regionToCurrency), func(i int) bool {
		return regionToCurrency[i].region >= x
	})
	if i < len(regionToCurrency) && regionToCurrency[i].region == x {
		return Unit{regionToCurrency[i].code}, true
	}
	return Unit{}, false
}

// FromTag reports the most likely currency for the given tag. It considers the
// currency defined in the -u extension and infers the region if necessary.
func FromTag(t language.Tag) (Unit, language.Confidence) {
	if cur := t.TypeForKey("cu"); len(cur) == 3 {
		c, _ := ParseISO(cur)
		return c, language.Exact
	}
	r, conf := t.Region()
	if cur, ok := FromRegion(r); ok {
		return cur, conf
	}
	return Unit{}, language.No
}

var (
	// Undefined and testing.
	XXX Unit = Unit{}
	XTS Unit = Unit{xts}

	// G10 currencies https://en.wikipedia.org/wiki/G10_currencies.
	USD Unit = Unit{usd}
	EUR Unit = Unit{eur}
	JPY Unit = Unit{jpy}
	GBP Unit = Unit{gbp}
	CHF Unit = Unit{chf}
	AUD Unit = Unit{aud}
	NZD Unit = Unit{nzd}
	CAD Unit = Unit{cad}
	SEK Unit = Unit{sek}
	NOK Unit = Unit{nok}

	// Additional common currencies as defined by CLDR.
	BRL Unit = Unit{brl}
	CNY Unit = Unit{cny}
	DKK Unit = Unit{dkk}
	INR Unit = Unit{inr}
	RUB Unit = Unit{rub}
	HKD Unit = Unit{hkd}
	IDR Unit = Unit{idr}
	KRW Unit = Unit{krw}
	MXN Unit = Unit{mxn}
	PLN Unit = Unit{pln}
	SAR Unit = Unit{sar}
	THB Unit = Unit{thb}
	TRY Unit = Unit{try}
	TWD Unit = Unit{twd}
	ZAR Unit = Unit{zar}

	// Precious metals.
	XAG Unit = Unit{xag}
	XAU Unit = Unit{xau}
	XPT Unit = Unit{xpt}
	XPD Unit = Unit{xpd}
)