github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/enum.go (about) 1 package fs 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7 ) 8 9 // Enum is an option which can only be one of the Choices. 10 // 11 // Suggested implementation is something like this: 12 // 13 // type choice = Enum[choices] 14 // 15 // const ( 16 // choiceA choice = iota 17 // choiceB 18 // choiceC 19 // ) 20 // 21 // type choices struct{} 22 // 23 // func (choices) Choices() []string { 24 // return []string{ 25 // choiceA: "A", 26 // choiceB: "B", 27 // choiceC: "C", 28 // } 29 // } 30 type Enum[C Choices] byte 31 32 // Choices returns the valid choices for this type. 33 // 34 // It must work on the zero value. 35 // 36 // Note that when using this in an Option the ExampleChoices will be 37 // filled in automatically. 38 type Choices interface { 39 // Choices returns the valid choices for this type 40 Choices() []string 41 } 42 43 // String renders the Enum as a string 44 func (e Enum[C]) String() string { 45 choices := e.Choices() 46 if int(e) >= len(choices) { 47 return fmt.Sprintf("Unknown(%d)", e) 48 } 49 return choices[e] 50 } 51 52 // Choices returns the possible values of the Enum. 53 func (e Enum[C]) Choices() []string { 54 var c C 55 return c.Choices() 56 } 57 58 // Help returns a comma separated list of all possible states. 59 func (e Enum[C]) Help() string { 60 return strings.Join(e.Choices(), ", ") 61 } 62 63 // Set the Enum entries 64 func (e *Enum[C]) Set(s string) error { 65 for i, choice := range e.Choices() { 66 if strings.EqualFold(s, choice) { 67 *e = Enum[C](i) 68 return nil 69 } 70 } 71 return fmt.Errorf("invalid choice %q from: %s", s, e.Help()) 72 } 73 74 // Type of the value. 75 // 76 // If C has a Type() string method then it will be used instead. 77 func (e Enum[C]) Type() string { 78 var c C 79 if do, ok := any(c).(typer); ok { 80 return do.Type() 81 } 82 return strings.Join(e.Choices(), "|") 83 } 84 85 // Scan implements the fmt.Scanner interface 86 func (e *Enum[C]) Scan(s fmt.ScanState, ch rune) error { 87 token, err := s.Token(true, nil) 88 if err != nil { 89 return err 90 } 91 return e.Set(string(token)) 92 } 93 94 // UnmarshalJSON parses it as a string or an integer 95 func (e *Enum[C]) UnmarshalJSON(in []byte) error { 96 choices := e.Choices() 97 return UnmarshalJSONFlag(in, e, func(i int64) error { 98 if i < 0 || i >= int64(len(choices)) { 99 return fmt.Errorf("%d is out of range: must be 0..%d", i, len(choices)) 100 } 101 *e = Enum[C](i) 102 return nil 103 }) 104 105 } 106 107 // MarshalJSON encodes it as string 108 func (e *Enum[C]) MarshalJSON() ([]byte, error) { 109 return json.Marshal(e.String()) 110 }