// 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. package currency import ( "fmt" "io" "sort" "gx/QmVgdgtv5rUcWWcHvHN1vdGkXi8UNztJ7JuT72YUJgG2ic/go-text/internal" "gx/QmVgdgtv5rUcWWcHvHN1vdGkXi8UNztJ7JuT72YUJgG2ic/go-text/internal/format" "gx/QmVgdgtv5rUcWWcHvHN1vdGkXi8UNztJ7JuT72YUJgG2ic/go-text/language" ) // Value is an amount-currency pair configured for language-specific formatting. type Value struct { amount interface{} currency Currency format *options } // Verify implementation var _ fmt.Formatter = Value{} // Currency reports the Currency of this value. func (v Value) Currency() Currency { return v.currency } // Amount reports the amount of this Value func (v Value) Amount() interface{} { return v.amount } var space = []byte(" ") // Format implements fmt.Formatter. It accepts format.State for // language-specific rendering. func (v Value) Format(s fmt.State, verb rune) { var lang int if state, ok := s.(format.State); ok { lang, _ = language.CompactIndex(state.Language()) } // Get the options. Use DefaultFormat if not present. opt := v.format if opt == nil { opt = defaultFormat } cur := v.currency if cur.index == 0 { cur = opt.currency } // TODO: use pattern. io.WriteString(s, opt.symbol(lang, cur)) if v.amount != nil { s.Write(space) // TODO: apply currency-specific rounding scale, _ := opt.kind.Rounding(cur) if _, ok := s.Precision(); !ok { fmt.Fprintf(s, "%.*f", scale, v.amount) } else { fmt.Fprint(s, v.amount) } } } // Formatter decorates a given number, Currency or Value with formatting options. type Formatter func(value interface{}) Value // TODO: call this a Formatter or FormatFunc? var dummy = USD.Value(0) // adjust creates a new Formatter based on the adjustments of fn on f. func (f Formatter) adjust(fn func(*options)) Formatter { var o options = *(f(dummy).format) fn(&o) return o.format } // Default creates a new Formatter that defaults to currency c if a numeric // value is passed that is not associated with a currency. func (f Formatter) Default(c Currency) Formatter { return f.adjust(func(o *options) { o.currency = c }) } // Kind sets the kind of the underlying currency. func (f Formatter) Kind(k Kind) Formatter { return f.adjust(func(o *options) { o.kind = k }) } var defaultFormat *options = ISO(dummy).format var ( // Uses Narrow symbols. Overrides Symbol, if present. NarrowSymbol Formatter = Formatter(formNarrow) // Use Symbols instead of ISO codes, when available. Symbol Formatter = Formatter(formSymbol) // Use ISO code as symbol. ISO Formatter = Formatter(formISO) // TODO: // // Use full name as symbol. // SpellOut Formatter // // // SpellOutAll causes symbol and numbers to be spelled wide. If used in // // combination with the Symbol option, the symbol will be written in ISO // // format. // SpellOutAll Formatter ) // options configures rendering and rounding options for a Value. type options struct { currency Currency kind Kind symbol func(compactIndex int, c Currency) string } func (o *options) format(value interface{}) Value { v := Value{format: o} switch x := value.(type) { case Value: v.currency = x.currency v.amount = x.amount case *Value: v.currency = x.currency v.amount = x.amount case Currency: v.currency = x case *Currency: v.currency = *x default: if o.currency.index == 0 { panic("cannot format number without a currency being set") } // TODO: Must be a number. v.amount = x } return v } var ( optISO = options{symbol: lookupISO} optSymbol = options{symbol: lookupSymbol} optNarrow = options{symbol: lookupNarrow} ) // These need to be functions, rather than curried methods, as curried methods // are evaluated at init time, causing tables to be included unconditionally. func formISO(x interface{}) Value { return optISO.format(x) } func formSymbol(x interface{}) Value { return optSymbol.format(x) } func formNarrow(x interface{}) Value { return optNarrow.format(x) } func lookupISO(x int, c Currency) string { return c.String() } func lookupSymbol(x int, c Currency) string { return normalSymbol.lookup(x, c) } func lookupNarrow(x int, c Currency) string { return narrowSymbol.lookup(x, c) } type symbolIndex struct { index []uint16 // position corresponds with compact index of language. data []curToIndex } var ( normalSymbol = symbolIndex{normalLangIndex, normalSymIndex} narrowSymbol = symbolIndex{narrowLangIndex, narrowSymIndex} ) func (x *symbolIndex) lookup(lang int, c Currency) string { for { index := x.data[x.index[lang]:x.index[lang+1]] i := sort.Search(len(index), func(i int) bool { return index[i].cur >= c.index }) if i < len(index) && index[i].cur == c.index { x := index[i].idx start := x + 1 end := start + uint16(symbols[x]) if start == end { return c.String() } return symbols[start:end] } if lang == 0 { break } lang = int(internal.Parent[lang]) } return c.String() }