go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/flag/stringmapflag/stringmapflag.go (about)

     1  // Copyright 2016 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package stringmapflag provides a flag.Value that, when parsed, augments a
    16  // map[string]string with the supplied parameter. The parameter is expressed as
    17  // a key[=value] option.
    18  //
    19  // # Example
    20  //
    21  // Assuming the flag option, "opt", is bound to a stringmapflag.Value, and the
    22  // following arguments are parsed:
    23  //
    24  //	-opt foo=bar
    25  //	-opt baz
    26  //
    27  // The resulting map would be equivalent to:
    28  // map[string]string {"foo": "bar", "baz": ""}
    29  package stringmapflag
    30  
    31  import (
    32  	"errors"
    33  	"flag"
    34  	"fmt"
    35  	"sort"
    36  	"strings"
    37  )
    38  
    39  // Value is a flag.Value implementation that stores arbitrary "key[=value]"
    40  // command-line flags as a string map.
    41  type Value map[string]string
    42  
    43  // Assert that Value conforms to the "flag.Value" interface.
    44  var _ = flag.Value(new(Value))
    45  
    46  func (v *Value) String() string {
    47  	if len(*v) == 0 {
    48  		return ""
    49  	}
    50  
    51  	keys := make([]string, 0, len(*v))
    52  	for k := range *v {
    53  		keys = append(keys, k)
    54  	}
    55  	sort.Strings(keys)
    56  
    57  	for idx, k := range keys {
    58  		if value := (*v)[k]; value != "" {
    59  			keys[idx] = fmt.Sprintf("%s=%s", k, value)
    60  		}
    61  	}
    62  	return strings.Join(keys, ",")
    63  }
    64  
    65  // Set implements flag.Value.
    66  func (v *Value) Set(key string) error {
    67  	key = strings.TrimSpace(key)
    68  	if len(key) == 0 {
    69  		return errors.New("cannot specify an empty k=v pair")
    70  	}
    71  
    72  	value := ""
    73  	idx := strings.Index(key, "=")
    74  	switch {
    75  	case idx == -1:
    76  	case idx == 0:
    77  		return errors.New("cannot have a k=v pair with empty key")
    78  
    79  	case idx > 0:
    80  		key, value = key[:idx], key[idx+1:]
    81  	}
    82  
    83  	// Add the entry to our map.
    84  	if len(*v) > 0 {
    85  		if _, ok := (*v)[key]; ok {
    86  			return fmt.Errorf("a value for key '%s' has already been defined", key)
    87  		}
    88  	}
    89  
    90  	// Record this k=v pair; create a new Value, if necessary.
    91  	if *v == nil {
    92  		*v = make(Value)
    93  	}
    94  	(*v)[key] = value
    95  	return nil
    96  }