2023-09-26 23:49:09 +08:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2023-09-27 22:31:47 +08:00
|
|
|
// UnmarshalJSON parses it as a string or an integer
|
2023-09-26 23:49:09 +08:00
|
|
|
func (e *Enum[C]) UnmarshalJSON(in []byte) error {
|
2023-09-27 22:31:47 +08:00
|
|
|
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
|
|
|
|
})
|
|
|
|
|
2023-09-26 23:49:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalJSON encodes it as string
|
|
|
|
func (e *Enum[C]) MarshalJSON() ([]byte, error) {
|
|
|
|
return json.Marshal(e.String())
|
|
|
|
}
|