3092f82dcc
This almost 100% backwards compatible. The only difference being that in the rc options/get output CutoffMode, LogLevel, TerminalColorMode will be output as strings instead of integers. This is a lot more convenient for the user. They still accept integer inputs though so the fallout from this should be minimal.
110 lines
2.3 KiB
Go
110 lines
2.3 KiB
Go
package fs
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Enum is an option which can only be one of the Choices.
|
|
//
|
|
// Suggested implementation is something like this:
|
|
//
|
|
// type choice = Enum[choices]
|
|
//
|
|
// const (
|
|
// choiceA choice = iota
|
|
// choiceB
|
|
// choiceC
|
|
// )
|
|
//
|
|
// type choices struct{}
|
|
//
|
|
// func (choices) Choices() []string {
|
|
// return []string{
|
|
// choiceA: "A",
|
|
// choiceB: "B",
|
|
// choiceC: "C",
|
|
// }
|
|
// }
|
|
type Enum[C Choices] byte
|
|
|
|
// Choices returns the valid choices for this type.
|
|
//
|
|
// It must work on the zero value.
|
|
//
|
|
// Note that when using this in an Option the ExampleChoices will be
|
|
// filled in automatically.
|
|
type Choices interface {
|
|
// Choices returns the valid choices for this type
|
|
Choices() []string
|
|
}
|
|
|
|
// String renders the Enum as a string
|
|
func (e Enum[C]) String() string {
|
|
choices := e.Choices()
|
|
if int(e) >= len(choices) {
|
|
return fmt.Sprintf("Unknown(%d)", e)
|
|
}
|
|
return choices[e]
|
|
}
|
|
|
|
// Choices returns the possible values of the Enum.
|
|
func (e Enum[C]) Choices() []string {
|
|
var c C
|
|
return c.Choices()
|
|
}
|
|
|
|
// Help returns a comma separated list of all possible states.
|
|
func (e Enum[C]) Help() string {
|
|
return strings.Join(e.Choices(), ", ")
|
|
}
|
|
|
|
// Set the Enum entries
|
|
func (e *Enum[C]) Set(s string) error {
|
|
for i, choice := range e.Choices() {
|
|
if strings.EqualFold(s, choice) {
|
|
*e = Enum[C](i)
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("invalid choice %q from: %s", s, e.Help())
|
|
}
|
|
|
|
// Type of the value.
|
|
//
|
|
// If C has a Type() string method then it will be used instead.
|
|
func (e Enum[C]) Type() string {
|
|
var c C
|
|
if do, ok := any(c).(typer); ok {
|
|
return do.Type()
|
|
}
|
|
return strings.Join(e.Choices(), "|")
|
|
}
|
|
|
|
// Scan implements the fmt.Scanner interface
|
|
func (e *Enum[C]) Scan(s fmt.ScanState, ch rune) error {
|
|
token, err := s.Token(true, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return e.Set(string(token))
|
|
}
|
|
|
|
// UnmarshalJSON parses it as a string or an integer
|
|
func (e *Enum[C]) UnmarshalJSON(in []byte) error {
|
|
choices := e.Choices()
|
|
return UnmarshalJSONFlag(in, e, func(i int64) error {
|
|
if i < 0 || i >= int64(len(choices)) {
|
|
return fmt.Errorf("%d is out of range: must be 0..%d", i, len(choices))
|
|
}
|
|
*e = Enum[C](i)
|
|
return nil
|
|
})
|
|
|
|
}
|
|
|
|
// MarshalJSON encodes it as string
|
|
func (e *Enum[C]) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(e.String())
|
|
}
|