1// Copyright 2017 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
5package number
6
7import (
8	"fmt"
9	"strings"
10
11	"golang.org/x/text/feature/plural"
12	"golang.org/x/text/internal/format"
13	"golang.org/x/text/internal/number"
14	"golang.org/x/text/language"
15)
16
17// A FormatFunc formats a number.
18type FormatFunc func(x interface{}, opts ...Option) Formatter
19
20// NewFormat creates a FormatFunc based on another FormatFunc and new options.
21// Use NewFormat to cash the creation of formatters.
22func NewFormat(format FormatFunc, opts ...Option) FormatFunc {
23	o := *format(nil).options
24	n := len(o.options)
25	o.options = append(o.options[:n:n], opts...)
26	return func(x interface{}, opts ...Option) Formatter {
27		return newFormatter(&o, opts, x)
28	}
29}
30
31type options struct {
32	verbs      string
33	initFunc   initFunc
34	options    []Option
35	pluralFunc func(t language.Tag, scale int) (f plural.Form, n int)
36}
37
38type optionFlag uint16
39
40const (
41	hasScale optionFlag = 1 << iota
42	hasPrecision
43	noSeparator
44	exact
45)
46
47type initFunc func(f *number.Formatter, t language.Tag)
48
49func newFormatter(o *options, opts []Option, value interface{}) Formatter {
50	if len(opts) > 0 {
51		n := *o
52		n.options = opts
53		o = &n
54	}
55	return Formatter{o, value}
56}
57
58func newOptions(verbs string, f initFunc) *options {
59	return &options{verbs: verbs, initFunc: f}
60}
61
62type Formatter struct {
63	*options
64	value interface{}
65}
66
67// Format implements format.Formatter. It is for internal use only for now.
68func (f Formatter) Format(state format.State, verb rune) {
69	// TODO: consider implementing fmt.Formatter instead and using the following
70	// piece of code. This allows numbers to be rendered mostly as expected
71	// when using fmt. But it may get weird with the spellout options and we
72	// may need more of format.State over time.
73	// lang := language.Und
74	// if s, ok := state.(format.State); ok {
75	// 	lang = s.Language()
76	// }
77
78	lang := state.Language()
79	if !strings.Contains(f.verbs, string(verb)) {
80		fmt.Fprintf(state, "%%!%s(%T=%v)", string(verb), f.value, f.value)
81		return
82	}
83	var p number.Formatter
84	f.initFunc(&p, lang)
85	for _, o := range f.options.options {
86		o(lang, &p)
87	}
88	if w, ok := state.Width(); ok {
89		p.FormatWidth = uint16(w)
90	}
91	if prec, ok := state.Precision(); ok {
92		switch verb {
93		case 'd':
94			p.SetScale(0)
95		case 'f':
96			p.SetScale(prec)
97		case 'e':
98			p.SetPrecision(prec + 1)
99		case 'g':
100			p.SetPrecision(prec)
101		}
102	}
103	var d number.Decimal
104	d.Convert(p.RoundingContext, f.value)
105	state.Write(p.Format(nil, &d))
106}
107
108// Digits returns information about which logical digits will be presented to
109// the user. This information is relevant, for instance, to determine plural
110// forms.
111func (f Formatter) Digits(buf []byte, tag language.Tag, scale int) number.Digits {
112	var p number.Formatter
113	f.initFunc(&p, tag)
114	if scale >= 0 {
115		// TODO: this only works well for decimal numbers, which is generally
116		// fine.
117		p.SetScale(scale)
118	}
119	var d number.Decimal
120	d.Convert(p.RoundingContext, f.value)
121	return number.FormatDigits(&d, p.RoundingContext)
122}
123