github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/text/currency/format.go (about) 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 }