1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
|
// Copyright 2022 Harald Albrecht.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package enumflag
import (
"slices"
"strings"
"github.com/spf13/cobra"
)
// enumSlice represents a slice of enumeration values that can be retrieved,
// set, and stringified.
type enumSlice[E comparable] struct {
v *[]E
merge bool // replace the complete slice or merge values?
}
// Get returns the slice enum values.
func (s *enumSlice[E]) Get() any { return *s.v }
// Set or merge one or more values of the new scalar enum value corresponding to
// the passed textual representation, using the additionally specified
// text-to-value mapping. If the specified textual representation doesn't match
// any of the defined ones, an error is returned instead and the value isn't
// changed. The first call to Set will always clear any previous default value.
// All subsequent calls to Set will merge the specified enum values with the
// current enum values.
func (s *enumSlice[E]) Set(val string, names enumMapper[E]) error {
// First parse and convert the textual enum values into their
// program-internal codes.
ids := strings.Split(val, ",")
enumvals := make([]E, 0, len(ids)) // ...educated guess
for _, id := range ids {
enumval, err := names.ValueOf(id)
if err != nil {
return err
}
enumvals = append(enumvals, enumval)
}
if !s.merge {
// Replace any existing default enum value set on first Set().
*s.v = enumvals
s.merge = true // ...and next time: merge.
return nil
}
// Later, merge with the existing enum values.
for _, enumval := range enumvals {
if slices.Index(*s.v, enumval) >= 0 {
continue
}
*s.v = append(*s.v, enumval)
}
return nil
}
// String returns the textual representation of the slice enum value, using the
// specified text-to-value mapping.
func (s *enumSlice[E]) String(names enumMapper[E]) string {
n := make([]string, 0, len(*s.v))
for _, enumval := range *s.v {
if enumnames := names.Lookup(enumval); len(enumnames) > 0 {
n = append(n, enumnames[0])
continue
}
n = append(n, unknown)
}
return "[" + strings.Join(n, ",") + "]"
}
// NewCompletor returns a cobra Completor that completes enum flag values.
func (s *enumSlice[E]) NewCompletor(enums EnumIdentifiers[E], help Help[E]) Completor {
completions := []string{}
for enumval, enumnames := range enums {
helptext := ""
if text, ok := help[enumval]; ok {
helptext = "\t" + text
}
// complete not only the canonical enum value name, but also all other
// (alias) names.
for _, name := range enumnames {
completions = append(completions, name+helptext)
}
}
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
prefix := ""
completes := []string{}
if lastComma := strings.LastIndex(toComplete, ","); lastComma >= 0 {
prefix = toComplete[:lastComma+1] // ...Prof J. won't ever like this variable name
completes = strings.Split(prefix, ",")
completes = completes[:len(completes)-1] // remove last empty element
}
filteredCompletions := make([]string, 0, len(completions))
for _, completion := range completions {
if slices.Contains(completes, strings.Split(completion, "\t")[0]) {
continue
}
filteredCompletions = append(filteredCompletions, prefix+completion)
}
return filteredCompletions, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
}
}
|