github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/settings/enum.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package settings
    12  
    13  import (
    14  	"bytes"
    15  	"fmt"
    16  	"sort"
    17  	"strconv"
    18  	"strings"
    19  
    20  	"github.com/cockroachdb/errors"
    21  )
    22  
    23  // EnumSetting is a StringSetting that restricts the values to be one of the `enumValues`
    24  type EnumSetting struct {
    25  	IntSetting
    26  	enumValues map[int64]string
    27  }
    28  
    29  var _ extendedSetting = &EnumSetting{}
    30  
    31  // Typ returns the short (1 char) string denoting the type of setting.
    32  func (e *EnumSetting) Typ() string {
    33  	return "e"
    34  }
    35  
    36  // String returns the enum's string value.
    37  func (e *EnumSetting) String(sv *Values) string {
    38  	enumID := e.Get(sv)
    39  	if str, ok := e.enumValues[enumID]; ok {
    40  		return str
    41  	}
    42  	return fmt.Sprintf("unknown(%d)", enumID)
    43  }
    44  
    45  // ParseEnum returns the enum value, and a boolean that indicates if it was parseable.
    46  func (e *EnumSetting) ParseEnum(raw string) (int64, bool) {
    47  	rawLower := strings.ToLower(raw)
    48  	for k, v := range e.enumValues {
    49  		if v == rawLower {
    50  			return k, true
    51  		}
    52  	}
    53  	// Attempt to parse the string as an integer since it isn't a valid enum string.
    54  	v, err := strconv.ParseInt(raw, 10, 64)
    55  	if err != nil {
    56  		return 0, false
    57  	}
    58  	_, ok := e.enumValues[v]
    59  	return v, ok
    60  }
    61  
    62  // GetAvailableValuesAsHint returns the possible enum settings as a string that
    63  // can be provided as an error hint to a user.
    64  func (e *EnumSetting) GetAvailableValuesAsHint() string {
    65  	// First stabilize output by sorting by key.
    66  	valIdxs := make([]int, 0, len(e.enumValues))
    67  	for i := range e.enumValues {
    68  		valIdxs = append(valIdxs, int(i))
    69  	}
    70  	sort.Ints(valIdxs)
    71  
    72  	// Now use those indices
    73  	vals := make([]string, 0, len(e.enumValues))
    74  	for _, enumIdx := range valIdxs {
    75  		vals = append(vals, fmt.Sprintf("%d: %s", enumIdx, e.enumValues[int64(enumIdx)]))
    76  	}
    77  	return "Available values: " + strings.Join(vals, ", ")
    78  }
    79  
    80  func (e *EnumSetting) set(sv *Values, k int64) error {
    81  	if _, ok := e.enumValues[k]; !ok {
    82  		return errors.Errorf("unrecognized value %d", k)
    83  	}
    84  	return e.IntSetting.set(sv, k)
    85  }
    86  
    87  func enumValuesToDesc(enumValues map[int64]string) string {
    88  	var buffer bytes.Buffer
    89  	values := make([]int64, 0, len(enumValues))
    90  	for k := range enumValues {
    91  		values = append(values, k)
    92  	}
    93  	sort.Slice(values, func(i, j int) bool { return values[i] < values[j] })
    94  
    95  	buffer.WriteString("[")
    96  	for i, k := range values {
    97  		if i > 0 {
    98  			buffer.WriteString(", ")
    99  		}
   100  		fmt.Fprintf(&buffer, "%s = %d", strings.ToLower(enumValues[k]), k)
   101  	}
   102  	buffer.WriteString("]")
   103  	return buffer.String()
   104  }
   105  
   106  // RegisterPublicEnumSetting defines a new setting with type int and makes it public.
   107  func RegisterPublicEnumSetting(
   108  	key, desc string, defaultValue string, enumValues map[int64]string,
   109  ) *EnumSetting {
   110  	s := RegisterEnumSetting(key, desc, defaultValue, enumValues)
   111  	s.SetVisibility(Public)
   112  	return s
   113  }
   114  
   115  // RegisterEnumSetting defines a new setting with type int.
   116  func RegisterEnumSetting(
   117  	key, desc string, defaultValue string, enumValues map[int64]string,
   118  ) *EnumSetting {
   119  	enumValuesLower := make(map[int64]string)
   120  	var i int64
   121  	var found bool
   122  	for k, v := range enumValues {
   123  		enumValuesLower[k] = strings.ToLower(v)
   124  		if v == defaultValue {
   125  			i = k
   126  			found = true
   127  		}
   128  	}
   129  
   130  	if !found {
   131  		panic(fmt.Sprintf("enum registered with default value %s not in map %s", defaultValue, enumValuesToDesc(enumValuesLower)))
   132  	}
   133  
   134  	setting := &EnumSetting{
   135  		IntSetting: IntSetting{defaultValue: i},
   136  		enumValues: enumValuesLower,
   137  	}
   138  
   139  	register(key, fmt.Sprintf("%s %s", desc, enumValuesToDesc(enumValues)), setting)
   140  	return setting
   141  }