...

Source file src/golang.org/x/text/currency/format.go

Documentation: golang.org/x/text/currency

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

View as plain text