OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / text / currency / format.go
1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package currency
6
7 import (
8         "fmt"
9         "io"
10         "sort"
11
12         "golang.org/x/text/internal"
13         "golang.org/x/text/internal/format"
14         "golang.org/x/text/language"
15 )
16
17 // Amount is an amount-currency unit pair.
18 type Amount struct {
19         amount   interface{} // Change to decimal(64|128).
20         currency Unit
21 }
22
23 // Currency reports the currency unit of this amount.
24 func (a Amount) Currency() Unit { return a.currency }
25
26 // TODO: based on decimal type, but may make sense to customize a bit.
27 // func (a Amount) Decimal()
28 // func (a Amount) Int() (int64, error)
29 // func (a Amount) Fraction() (int64, error)
30 // func (a Amount) Rat() *big.Rat
31 // func (a Amount) Float() (float64, error)
32 // func (a Amount) Scale() uint
33 // func (a Amount) Precision() uint
34 // func (a Amount) Sign() int
35 //
36 // Add/Sub/Div/Mul/Round.
37
38 var space = []byte(" ")
39
40 // Format implements fmt.Formatter. It accepts format.State for
41 // language-specific rendering.
42 func (a Amount) Format(s fmt.State, verb rune) {
43         v := formattedValue{
44                 currency: a.currency,
45                 amount:   a.amount,
46                 format:   defaultFormat,
47         }
48         v.Format(s, verb)
49 }
50
51 // formattedValue is currency amount or unit that implements language-sensitive
52 // formatting.
53 type formattedValue struct {
54         currency Unit
55         amount   interface{} // Amount, Unit, or number.
56         format   *options
57 }
58
59 // Format implements fmt.Formatter. It accepts format.State for
60 // language-specific rendering.
61 func (v formattedValue) Format(s fmt.State, verb rune) {
62         var lang int
63         if state, ok := s.(format.State); ok {
64                 lang, _ = language.CompactIndex(state.Language())
65         }
66
67         // Get the options. Use DefaultFormat if not present.
68         opt := v.format
69         if opt == nil {
70                 opt = defaultFormat
71         }
72         cur := v.currency
73         if cur.index == 0 {
74                 cur = opt.currency
75         }
76
77         // TODO: use pattern.
78         io.WriteString(s, opt.symbol(lang, cur))
79         if v.amount != nil {
80                 s.Write(space)
81
82                 // TODO: apply currency-specific rounding
83                 scale, _ := opt.kind.Rounding(cur)
84                 if _, ok := s.Precision(); !ok {
85                         fmt.Fprintf(s, "%.*f", scale, v.amount)
86                 } else {
87                         fmt.Fprint(s, v.amount)
88                 }
89         }
90 }
91
92 // Formatter decorates a given number, Unit or Amount with formatting options.
93 type Formatter func(amount interface{}) formattedValue
94
95 // func (f Formatter) Options(opts ...Option) Formatter
96
97 // TODO: call this a Formatter or FormatFunc?
98
99 var dummy = USD.Amount(0)
100
101 // adjust creates a new Formatter based on the adjustments of fn on f.
102 func (f Formatter) adjust(fn func(*options)) Formatter {
103         var o options = *(f(dummy).format)
104         fn(&o)
105         return o.format
106 }
107
108 // Default creates a new Formatter that defaults to currency unit c if a numeric
109 // value is passed that is not associated with a currency.
110 func (f Formatter) Default(currency Unit) Formatter {
111         return f.adjust(func(o *options) { o.currency = currency })
112 }
113
114 // Kind sets the kind of the underlying currency unit.
115 func (f Formatter) Kind(k Kind) Formatter {
116         return f.adjust(func(o *options) { o.kind = k })
117 }
118
119 var defaultFormat *options = ISO(dummy).format
120
121 var (
122         // Uses Narrow symbols. Overrides Symbol, if present.
123         NarrowSymbol Formatter = Formatter(formNarrow)
124
125         // Use Symbols instead of ISO codes, when available.
126         Symbol Formatter = Formatter(formSymbol)
127
128         // Use ISO code as symbol.
129         ISO Formatter = Formatter(formISO)
130
131         // TODO:
132         // // Use full name as symbol.
133         // Name Formatter
134 )
135
136 // options configures rendering and rounding options for an Amount.
137 type options struct {
138         currency Unit
139         kind     Kind
140
141         symbol func(compactIndex int, c Unit) string
142 }
143
144 func (o *options) format(amount interface{}) formattedValue {
145         v := formattedValue{format: o}
146         switch x := amount.(type) {
147         case Amount:
148                 v.amount = x.amount
149                 v.currency = x.currency
150         case *Amount:
151                 v.amount = x.amount
152                 v.currency = x.currency
153         case Unit:
154                 v.currency = x
155         case *Unit:
156                 v.currency = *x
157         default:
158                 if o.currency.index == 0 {
159                         panic("cannot format number without a currency being set")
160                 }
161                 // TODO: Must be a number.
162                 v.amount = x
163                 v.currency = o.currency
164         }
165         return v
166 }
167
168 var (
169         optISO    = options{symbol: lookupISO}
170         optSymbol = options{symbol: lookupSymbol}
171         optNarrow = options{symbol: lookupNarrow}
172 )
173
174 // These need to be functions, rather than curried methods, as curried methods
175 // are evaluated at init time, causing tables to be included unconditionally.
176 func formISO(x interface{}) formattedValue    { return optISO.format(x) }
177 func formSymbol(x interface{}) formattedValue { return optSymbol.format(x) }
178 func formNarrow(x interface{}) formattedValue { return optNarrow.format(x) }
179
180 func lookupISO(x int, c Unit) string    { return c.String() }
181 func lookupSymbol(x int, c Unit) string { return normalSymbol.lookup(x, c) }
182 func lookupNarrow(x int, c Unit) string { return narrowSymbol.lookup(x, c) }
183
184 type symbolIndex struct {
185         index []uint16 // position corresponds with compact index of language.
186         data  []curToIndex
187 }
188
189 var (
190         normalSymbol = symbolIndex{normalLangIndex, normalSymIndex}
191         narrowSymbol = symbolIndex{narrowLangIndex, narrowSymIndex}
192 )
193
194 func (x *symbolIndex) lookup(lang int, c Unit) string {
195         for {
196                 index := x.data[x.index[lang]:x.index[lang+1]]
197                 i := sort.Search(len(index), func(i int) bool {
198                         return index[i].cur >= c.index
199                 })
200                 if i < len(index) && index[i].cur == c.index {
201                         x := index[i].idx
202                         start := x + 1
203                         end := start + uint16(symbols[x])
204                         if start == end {
205                                 return c.String()
206                         }
207                         return symbols[start:end]
208                 }
209                 if lang == 0 {
210                         break
211                 }
212                 lang = int(internal.Parent[lang])
213         }
214         return c.String()
215 }