github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/golang.org/x/text/feature/plural/message.go (about)

     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  
     5  package plural
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"reflect"
    11  	"strconv"
    12  
    13  	"golang.org/x/text/internal/catmsg"
    14  	"golang.org/x/text/internal/number"
    15  	"golang.org/x/text/language"
    16  	"golang.org/x/text/message/catalog"
    17  )
    18  
    19  // TODO: consider deleting this interface. Maybe VisibleDigits is always
    20  // sufficient and practical.
    21  
    22  // Interface is used for types that can determine their own plural form.
    23  type Interface interface {
    24  	// PluralForm reports the plural form for the given language of the
    25  	// underlying value. It also returns the integer value. If the integer value
    26  	// is larger than fits in n, PluralForm may return a value modulo
    27  	// 10,000,000.
    28  	PluralForm(t language.Tag, scale int) (f Form, n int)
    29  }
    30  
    31  // Selectf returns the first case for which its selector is a match for the
    32  // arg-th substitution argument to a formatting call, formatting it as indicated
    33  // by format.
    34  //
    35  // The cases argument are pairs of selectors and messages. Selectors are of type
    36  // string or Form. Messages are of type string or catalog.Message. A selector
    37  // matches an argument if:
    38  //   - it is "other" or Other
    39  //   - it matches the plural form of the argument: "zero", "one", "two", "few",
    40  //     or "many", or the equivalent Form
    41  //   - it is of the form "=x" where x is an integer that matches the value of
    42  //     the argument.
    43  //   - it is of the form "<x" where x is an integer that is larger than the
    44  //     argument.
    45  //
    46  // The format argument determines the formatting parameters for which to
    47  // determine the plural form. This is especially relevant for non-integer
    48  // values.
    49  //
    50  // The format string may be "", in which case a best-effort attempt is made to
    51  // find a reasonable representation on which to base the plural form. Examples
    52  // of format strings are:
    53  //   - %.2f   decimal with scale 2
    54  //   - %.2e   scientific notation with precision 3 (scale + 1)
    55  //   - %d     integer
    56  func Selectf(arg int, format string, cases ...interface{}) catalog.Message {
    57  	var p parser
    58  	// Intercept the formatting parameters of format by doing a dummy print.
    59  	fmt.Fprintf(io.Discard, format, &p)
    60  	m := &message{arg, kindDefault, 0, cases}
    61  	switch p.verb {
    62  	case 'g':
    63  		m.kind = kindPrecision
    64  		m.scale = p.scale
    65  	case 'f':
    66  		m.kind = kindScale
    67  		m.scale = p.scale
    68  	case 'e':
    69  		m.kind = kindScientific
    70  		m.scale = p.scale
    71  	case 'd':
    72  		m.kind = kindScale
    73  		m.scale = 0
    74  	default:
    75  		// TODO: do we need to handle errors?
    76  	}
    77  	return m
    78  }
    79  
    80  type parser struct {
    81  	verb  rune
    82  	scale int
    83  }
    84  
    85  func (p *parser) Format(s fmt.State, verb rune) {
    86  	p.verb = verb
    87  	p.scale = -1
    88  	if prec, ok := s.Precision(); ok {
    89  		p.scale = prec
    90  	}
    91  }
    92  
    93  type message struct {
    94  	arg   int
    95  	kind  int
    96  	scale int
    97  	cases []interface{}
    98  }
    99  
   100  const (
   101  	// Start with non-ASCII to allow skipping values.
   102  	kindDefault    = 0x80 + iota
   103  	kindScale      // verb f, number of fraction digits follows
   104  	kindScientific // verb e, number of fraction digits follows
   105  	kindPrecision  // verb g, number of significant digits follows
   106  )
   107  
   108  var handle = catmsg.Register("golang.org/x/text/feature/plural:plural", execute)
   109  
   110  func (m *message) Compile(e *catmsg.Encoder) error {
   111  	e.EncodeMessageType(handle)
   112  
   113  	e.EncodeUint(uint64(m.arg))
   114  
   115  	e.EncodeUint(uint64(m.kind))
   116  	if m.kind > kindDefault {
   117  		e.EncodeUint(uint64(m.scale))
   118  	}
   119  
   120  	forms := validForms(cardinal, e.Language())
   121  
   122  	for i := 0; i < len(m.cases); {
   123  		if err := compileSelector(e, forms, m.cases[i]); err != nil {
   124  			return err
   125  		}
   126  		if i++; i >= len(m.cases) {
   127  			return fmt.Errorf("plural: no message defined for selector %v", m.cases[i-1])
   128  		}
   129  		var msg catalog.Message
   130  		switch x := m.cases[i].(type) {
   131  		case string:
   132  			msg = catalog.String(x)
   133  		case catalog.Message:
   134  			msg = x
   135  		default:
   136  			return fmt.Errorf("plural: message of type %T; must be string or catalog.Message", x)
   137  		}
   138  		if err := e.EncodeMessage(msg); err != nil {
   139  			return err
   140  		}
   141  		i++
   142  	}
   143  	return nil
   144  }
   145  
   146  func compileSelector(e *catmsg.Encoder, valid []Form, selector interface{}) error {
   147  	form := Other
   148  	switch x := selector.(type) {
   149  	case string:
   150  		if x == "" {
   151  			return fmt.Errorf("plural: empty selector")
   152  		}
   153  		if c := x[0]; c == '=' || c == '<' {
   154  			val, err := strconv.ParseUint(x[1:], 10, 16)
   155  			if err != nil {
   156  				return fmt.Errorf("plural: invalid number in selector %q: %v", selector, err)
   157  			}
   158  			e.EncodeUint(uint64(c))
   159  			e.EncodeUint(val)
   160  			return nil
   161  		}
   162  		var ok bool
   163  		form, ok = countMap[x]
   164  		if !ok {
   165  			return fmt.Errorf("plural: invalid plural form %q", selector)
   166  		}
   167  	case Form:
   168  		form = x
   169  	default:
   170  		return fmt.Errorf("plural: selector of type %T; want string or Form", selector)
   171  	}
   172  
   173  	ok := false
   174  	for _, f := range valid {
   175  		if f == form {
   176  			ok = true
   177  			break
   178  		}
   179  	}
   180  	if !ok {
   181  		return fmt.Errorf("plural: form %q not supported for language %q", selector, e.Language())
   182  	}
   183  	e.EncodeUint(uint64(form))
   184  	return nil
   185  }
   186  
   187  func execute(d *catmsg.Decoder) bool {
   188  	lang := d.Language()
   189  	argN := int(d.DecodeUint())
   190  	kind := int(d.DecodeUint())
   191  	scale := -1 // default
   192  	if kind > kindDefault {
   193  		scale = int(d.DecodeUint())
   194  	}
   195  	form := Other
   196  	n := -1
   197  	if arg := d.Arg(argN); arg == nil {
   198  		// Default to Other.
   199  	} else if x, ok := arg.(number.VisibleDigits); ok {
   200  		d := x.Digits(nil, lang, scale)
   201  		form, n = cardinal.matchDisplayDigits(lang, &d)
   202  	} else if x, ok := arg.(Interface); ok {
   203  		// This covers lists and formatters from the number package.
   204  		form, n = x.PluralForm(lang, scale)
   205  	} else {
   206  		var f number.Formatter
   207  		switch kind {
   208  		case kindScale:
   209  			f.InitDecimal(lang)
   210  			f.SetScale(scale)
   211  		case kindScientific:
   212  			f.InitScientific(lang)
   213  			f.SetScale(scale)
   214  		case kindPrecision:
   215  			f.InitDecimal(lang)
   216  			f.SetPrecision(scale)
   217  		case kindDefault:
   218  			// sensible default
   219  			f.InitDecimal(lang)
   220  			if k := reflect.TypeOf(arg).Kind(); reflect.Int <= k && k <= reflect.Uintptr {
   221  				f.SetScale(0)
   222  			} else {
   223  				f.SetScale(2)
   224  			}
   225  		}
   226  		var dec number.Decimal // TODO: buffer in Printer
   227  		dec.Convert(f.RoundingContext, arg)
   228  		v := number.FormatDigits(&dec, f.RoundingContext)
   229  		if !v.NaN && !v.Inf {
   230  			form, n = cardinal.matchDisplayDigits(d.Language(), &v)
   231  		}
   232  	}
   233  	for !d.Done() {
   234  		f := d.DecodeUint()
   235  		if (f == '=' && n == int(d.DecodeUint())) ||
   236  			(f == '<' && 0 <= n && n < int(d.DecodeUint())) ||
   237  			form == Form(f) ||
   238  			Other == Form(f) {
   239  			return d.ExecuteMessage()
   240  		}
   241  		d.SkipMessage()
   242  	}
   243  	return false
   244  }