// 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) }