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  }