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  }