kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/util/flagutil/flagutil.go (about)

     1  /*
     2   * Copyright 2015 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // Package flagutil is a collection of helper functions for Kythe binaries using
    18  // the flag package.
    19  package flagutil // import "kythe.io/kythe/go/util/flagutil"
    20  
    21  import (
    22  	"flag"
    23  	"fmt"
    24  	"os"
    25  	"path/filepath"
    26  	"sort"
    27  	"strconv"
    28  	"strings"
    29  
    30  	"kythe.io/kythe/go/util/build"
    31  
    32  	"bitbucket.org/creachadair/stringset"
    33  )
    34  
    35  // SimpleUsage returns a basic flag.Usage function that prints the given
    36  // description and list of arguments in the following format:
    37  //
    38  //	Usage: binary <arg0> <arg1> ... <argN>
    39  //	<description>
    40  //
    41  //	<build.VersionLine()>
    42  //
    43  //	Flags:
    44  //	<flag.PrintDefaults()>
    45  func SimpleUsage(description string, args ...string) func() {
    46  	return func() {
    47  		prefix := fmt.Sprintf("Usage: %s ", filepath.Base(os.Args[0]))
    48  		alignArgs(len(prefix), args)
    49  		fmt.Fprintf(os.Stderr, `%s%s
    50  %s
    51  
    52  %s
    53  
    54  Flags:
    55  `, prefix, strings.Join(args, " "), description, build.VersionLine())
    56  		flag.PrintDefaults()
    57  	}
    58  }
    59  
    60  func alignArgs(col int, args []string) {
    61  	s := strings.Repeat(" ", col)
    62  	for i, arg := range args {
    63  		args[i] = strings.Replace(arg, "\n", "\n"+s, -1)
    64  	}
    65  }
    66  
    67  // UsageError prints msg to stderr, calls flag.Usage, and exits the program
    68  // unsuccessfully.
    69  func UsageError(msg string) {
    70  	fmt.Fprintln(os.Stderr, "ERROR: "+msg)
    71  	flag.Usage()
    72  	os.Exit(1)
    73  }
    74  
    75  // UsageErrorf prints str formatted with the given vals to stderr, calls
    76  // flag.Usage, and exits the program unsuccessfully.
    77  func UsageErrorf(str string, vals ...any) {
    78  	UsageError(fmt.Sprintf(str, vals...))
    79  }
    80  
    81  // StringList implements a flag.Value that accepts an sequence of values as a CSV.
    82  type StringList []string
    83  
    84  // Set implements part of the flag.Getter interface and will append new values to the flag.
    85  func (f *StringList) Set(s string) error {
    86  	*f = append(*f, strings.Split(s, ",")...)
    87  	return nil
    88  }
    89  
    90  // String implements part of the flag.Getter interface and returns a string-ish value for the flag.
    91  func (f *StringList) String() string {
    92  	if f == nil {
    93  		return ""
    94  	}
    95  	return strings.Join(*f, ",")
    96  }
    97  
    98  // Get implements flag.Getter and returns a slice of string values.
    99  func (f *StringList) Get() any {
   100  	if f == nil {
   101  		return []string(nil)
   102  	}
   103  	return *f
   104  }
   105  
   106  // StringSet implements a flag.Value that accepts an set of values as a CSV.
   107  type StringSet stringset.Set
   108  
   109  // Set implements part of the flag.Getter interface and will append new values to the flag.
   110  func (f *StringSet) Set(s string) error {
   111  	(*stringset.Set)(f).Add(strings.Split(s, ",")...)
   112  	return nil
   113  }
   114  
   115  // Update adds the values from other to the contained stringset.
   116  func (f *StringSet) Update(o StringSet) bool {
   117  	return (*stringset.Set)(f).Update(stringset.Set(o))
   118  }
   119  
   120  // Elements returns the set of elements as a sorted slice.
   121  func (f *StringSet) Elements() []string {
   122  	return (*stringset.Set)(f).Elements()
   123  }
   124  
   125  // Len returns the number of elements.
   126  func (f *StringSet) Len() int {
   127  	return (*stringset.Set)(f).Len()
   128  }
   129  
   130  // String implements part of the flag.Getter interface and returns a string-ish value for the flag.
   131  func (f *StringSet) String() string {
   132  	if f == nil {
   133  		return ""
   134  	}
   135  	return strings.Join(f.Elements(), ",")
   136  }
   137  
   138  // Get implements flag.Getter and returns a slice of string values.
   139  func (f *StringSet) Get() any {
   140  	if f == nil {
   141  		return stringset.Set(nil)
   142  	}
   143  	return *f
   144  }
   145  
   146  // StringMultimap implements a flag.Value that accepts an set of key-value entries as a CSV.
   147  type StringMultimap map[string]stringset.Set
   148  
   149  // Set implements part of the flag.Getter interface and will append new values to the flag.
   150  func (f *StringMultimap) Set(s string) error {
   151  	if *f == nil {
   152  		*f = make(map[string]stringset.Set)
   153  	}
   154  	m := *f
   155  	for _, e := range strings.Split(s, ",") {
   156  		pair := strings.SplitN(e, "=", 2)
   157  		if len(pair) != 2 {
   158  			return fmt.Errorf("invalid key-value entry: %q", e)
   159  		}
   160  		key, val := pair[0], pair[1]
   161  		set := m[key]
   162  		if set == nil {
   163  			set = stringset.New()
   164  			m[key] = set
   165  		}
   166  		set.Add(val)
   167  	}
   168  	return nil
   169  }
   170  
   171  // String implements part of the flag.Getter interface and returns a string-ish value for the flag.
   172  func (f *StringMultimap) String() string {
   173  	if f == nil || *f == nil {
   174  		return "{}"
   175  	}
   176  	entries := make([]string, 0, len(*f))
   177  	for k, vs := range *f {
   178  		for _, v := range vs.Elements() {
   179  			entries = append(entries, fmt.Sprintf("%s=%s", k, v))
   180  		}
   181  	}
   182  	sort.Strings(entries)
   183  	return strings.Join(entries, ",")
   184  }
   185  
   186  // Get implements flag.Getter and returns a slice of string values.
   187  func (f *StringMultimap) Get() any {
   188  	if f == nil {
   189  		return map[string]stringset.Set(nil)
   190  	}
   191  	return *f
   192  }
   193  
   194  // IntList implements a flag.Value that accepts multiple values by repeatedly specifying the flag.
   195  type IntList []int
   196  
   197  // String returns a string representation of the flag's value.
   198  func (i *IntList) String() string { return fmt.Sprintf("%v", *i) }
   199  
   200  // Set adds a value to the flag's list of integers.
   201  func (i *IntList) Set(value string) error {
   202  	v, err := strconv.Atoi(value)
   203  	if err != nil {
   204  		return err
   205  	}
   206  	*i = append(*i, v)
   207  	return nil
   208  }