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 10 "golang.org/x/text/internal/number" 11 "golang.org/x/text/language" 12) 13 14// An Option configures a Formatter. 15type Option option 16 17type option func(tag language.Tag, f *number.Formatter) 18 19// TODO: SpellOut requires support of the ICU RBNF format. 20// func SpellOut() Option 21 22// NoSeparator causes a number to be displayed without grouping separators. 23func NoSeparator() Option { 24 return func(t language.Tag, f *number.Formatter) { 25 f.GroupingSize = [2]uint8{} 26 } 27} 28 29// MaxIntegerDigits limits the number of integer digits, eliminating the 30// most significant digits. 31func MaxIntegerDigits(max int) Option { 32 return func(t language.Tag, f *number.Formatter) { 33 if max >= 1<<8 { 34 max = (1 << 8) - 1 35 } 36 f.MaxIntegerDigits = uint8(max) 37 } 38} 39 40// MinIntegerDigits specifies the minimum number of integer digits, adding 41// leading zeros when needed. 42func MinIntegerDigits(min int) Option { 43 return func(t language.Tag, f *number.Formatter) { 44 if min >= 1<<8 { 45 min = (1 << 8) - 1 46 } 47 f.MinIntegerDigits = uint8(min) 48 } 49} 50 51// MaxFractionDigits specifies the maximum number of fractional digits. 52func MaxFractionDigits(max int) Option { 53 return func(t language.Tag, f *number.Formatter) { 54 if max >= 1<<15 { 55 max = (1 << 15) - 1 56 } 57 f.MaxFractionDigits = int16(max) 58 } 59} 60 61// MinFractionDigits specifies the minimum number of fractional digits. 62func MinFractionDigits(min int) Option { 63 return func(t language.Tag, f *number.Formatter) { 64 if min >= 1<<8 { 65 min = (1 << 8) - 1 66 } 67 f.MinFractionDigits = uint8(min) 68 } 69} 70 71// Precision sets the maximum number of significant digits. A negative value 72// means exact. 73func Precision(prec int) Option { 74 return func(t language.Tag, f *number.Formatter) { 75 f.SetPrecision(prec) 76 } 77} 78 79// Scale simultaneously sets MinFractionDigits and MaxFractionDigits to the 80// given value. 81func Scale(decimals int) Option { 82 return func(t language.Tag, f *number.Formatter) { 83 f.SetScale(decimals) 84 } 85} 86 87// IncrementString sets the incremental value to which numbers should be 88// rounded. For instance: Increment("0.05") will cause 1.44 to round to 1.45. 89// IncrementString also sets scale to the scale of the increment. 90func IncrementString(decimal string) Option { 91 increment := 0 92 scale := 0 93 d := decimal 94 p := 0 95 for ; p < len(d) && '0' <= d[p] && d[p] <= '9'; p++ { 96 increment *= 10 97 increment += int(d[p]) - '0' 98 } 99 if p < len(d) && d[p] == '.' { 100 for p++; p < len(d) && '0' <= d[p] && d[p] <= '9'; p++ { 101 increment *= 10 102 increment += int(d[p]) - '0' 103 scale++ 104 } 105 } 106 if p < len(d) { 107 increment = 0 108 scale = 0 109 } 110 return func(t language.Tag, f *number.Formatter) { 111 f.Increment = uint32(increment) 112 f.IncrementScale = uint8(scale) 113 f.SetScale(scale) 114 } 115} 116 117func noop(language.Tag, *number.Formatter) {} 118 119// PatternOverrides allows users to specify alternative patterns for specific 120// languages. The Pattern will be overridden for all languages in a subgroup as 121// well. The function will panic for invalid input. It is best to create this 122// option at startup time. 123// PatternOverrides must be the first Option passed to a formatter. 124func PatternOverrides(patterns map[string]string) Option { 125 // TODO: make it so that it does not have to be the first option. 126 // TODO: use -x-nochild to indicate it does not override child tags. 127 m := map[language.Tag]*number.Pattern{} 128 for k, v := range patterns { 129 tag := language.MustParse(k) 130 p, err := number.ParsePattern(v) 131 if err != nil { 132 panic(fmt.Errorf("number: PatternOverrides: %v", err)) 133 } 134 m[tag] = p 135 } 136 return func(t language.Tag, f *number.Formatter) { 137 // TODO: Use language grouping relation instead of parent relation. 138 // TODO: Should parent implement the grouping relation? 139 for lang := t; ; lang = t.Parent() { 140 if p, ok := m[lang]; ok { 141 f.Pattern = *p 142 break 143 } 144 if lang == language.Und { 145 break 146 } 147 } 148 } 149} 150 151// FormatWidth sets the total format width. 152func FormatWidth(n int) Option { 153 if n <= 0 { 154 return noop 155 } 156 return func(t language.Tag, f *number.Formatter) { 157 f.FormatWidth = uint16(n) 158 if f.PadRune == 0 { 159 f.PadRune = ' ' 160 } 161 } 162} 163 164// Pad sets the rune to be used for filling up to the format width. 165func Pad(r rune) Option { 166 return func(t language.Tag, f *number.Formatter) { 167 f.PadRune = r 168 } 169} 170 171// TODO: 172// - FormatPosition (using type aliasing?) 173// - Multiplier: find a better way to represent and figure out what to do 174// with clashes with percent/permille. 175// - NumberingSystem(nu string): not accessable in number.Info now. Also, should 176// this be keyed by language or generic? 177// - SymbolOverrides(symbols map[string]map[number.SymbolType]string) Option 178