123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- // Copyright 2017 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 number
- import (
- "fmt"
- "strings"
- "golang.org/x/text/feature/plural"
- "golang.org/x/text/internal/format"
- "golang.org/x/text/internal/number"
- "golang.org/x/text/language"
- )
- // A FormatFunc formates a number.
- type FormatFunc func(x interface{}, opts ...Option) Formatter
- // NewFormat creates a FormatFunc based on another FormatFunc and new options.
- // Use NewFormat to cash the creation of formatters.
- func NewFormat(format FormatFunc, opts ...Option) FormatFunc {
- o := *format(nil).options
- n := len(o.options)
- o.options = append(o.options[:n:n], opts...)
- return func(x interface{}, opts ...Option) Formatter {
- return newFormatter(&o, opts, x)
- }
- }
- type options struct {
- verbs string
- initFunc initFunc
- options []Option
- pluralFunc func(t language.Tag, scale int) (f plural.Form, n int)
- }
- type optionFlag uint16
- const (
- hasScale optionFlag = 1 << iota
- hasPrecision
- noSeparator
- exact
- )
- type initFunc func(f *number.Formatter, t language.Tag)
- func newFormatter(o *options, opts []Option, value interface{}) Formatter {
- if len(opts) > 0 {
- n := *o
- n.options = opts
- o = &n
- }
- return Formatter{o, value}
- }
- func newOptions(verbs string, f initFunc) *options {
- return &options{verbs: verbs, initFunc: f}
- }
- type Formatter struct {
- *options
- value interface{}
- }
- // Format implements format.Formatter. It is for internal use only for now.
- func (f Formatter) Format(state format.State, verb rune) {
- // TODO: consider implementing fmt.Formatter instead and using the following
- // piece of code. This allows numbers to be rendered mostly as expected
- // when using fmt. But it may get weird with the spellout options and we
- // may need more of format.State over time.
- // lang := language.Und
- // if s, ok := state.(format.State); ok {
- // lang = s.Language()
- // }
- lang := state.Language()
- if !strings.Contains(f.verbs, string(verb)) {
- fmt.Fprintf(state, "%%!%s(%T=%v)", string(verb), f.value, f.value)
- return
- }
- var p number.Formatter
- f.initFunc(&p, lang)
- for _, o := range f.options.options {
- o(lang, &p)
- }
- if w, ok := state.Width(); ok {
- p.FormatWidth = uint16(w)
- }
- if prec, ok := state.Precision(); ok {
- switch verb {
- case 'd':
- p.SetScale(0)
- case 'f':
- p.SetScale(prec)
- case 'e':
- p.SetPrecision(prec + 1)
- case 'g':
- p.SetPrecision(prec)
- }
- }
- var d number.Decimal
- d.Convert(p.RoundingContext, f.value)
- state.Write(p.Format(nil, &d))
- }
- // Digits returns information about which logical digits will be presented to
- // the user. This information is relevant, for instance, to determine plural
- // forms.
- func (f Formatter) Digits(buf []byte, tag language.Tag, scale int) number.Digits {
- var p number.Formatter
- f.initFunc(&p, tag)
- if scale >= 0 {
- // TODO: this only works well for decimal numbers, which is generally
- // fine.
- p.SetScale(scale)
- }
- var d number.Decimal
- d.Convert(p.RoundingContext, f.value)
- return number.FormatDigits(&d, p.RoundingContext)
- }
|