vitess.io/vitess@v0.16.2/go/internal/flag/usage.go (about)

     1  /*
     2  Copyright 2022 The Vitess Authors.
     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  package flag
    17  
    18  import (
    19  	goflag "flag"
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"strings"
    24  )
    25  
    26  // UsageOptions controls the custom behavior when overriding the Usage for a
    27  // FlagSet.
    28  type UsageOptions struct {
    29  	// Preface determines the beginning of the help text, before flag usages
    30  	// and defaults. If this function is nil, the Usage will print "Usage of <os.Args[0]:\n".
    31  	Preface func(w io.Writer)
    32  	// Epilogue optionally prints text after the flag usages and defaults. If
    33  	// this function is nil, the flag usage/defaults will be the end of the
    34  	// Usage text.
    35  	Epilogue func(w io.Writer)
    36  	// FlagFilter allows certain flags to be omitted from the flag usage and
    37  	// defaults. If non-nil, flags for which this function returns false are
    38  	// omitted.
    39  	FlagFilter func(f *goflag.Flag) bool
    40  }
    41  
    42  // SetUsage sets the Usage function for the given FlagSet according to the
    43  // options. For VEP-4, all flags are printed in their double-dash form.
    44  func SetUsage(fs *goflag.FlagSet, opts UsageOptions) {
    45  	flagFilter := opts.FlagFilter
    46  	if flagFilter == nil {
    47  		flagFilter = func(f *goflag.Flag) bool { return true }
    48  	}
    49  
    50  	fs.Usage = func() {
    51  		switch opts.Preface {
    52  		case nil:
    53  			fmt.Fprintf(fs.Output(), "Usage of %s:\n", os.Args[0])
    54  		default:
    55  			opts.Preface(fs.Output())
    56  		}
    57  
    58  		var buf strings.Builder
    59  		fs.VisitAll(func(f *goflag.Flag) {
    60  			if !flagFilter(f) {
    61  				return
    62  			}
    63  
    64  			defer buf.Reset()
    65  			defer func() { fmt.Fprintf(fs.Output(), "%s\n", buf.String()) }()
    66  
    67  			// See https://cs.opensource.google/go/go/+/refs/tags/go1.17.7:src/flag/flag.go;l=512;drc=refs%2Ftags%2Fgo1.17.7
    68  			// for why two leading spaces.
    69  			buf.WriteString("  ")
    70  
    71  			// We use `UnquoteUsage` to preserve the "name override"
    72  			// behavior of the standard flag package, documented here:
    73  			//
    74  			//	> The listed type, here int, can be changed by placing a
    75  			//	> back-quoted name in the flag's usage string; the first
    76  			//	> such item in the message is taken to be a parameter name
    77  			//	> to show in the message and the back quotes are stripped
    78  			//	> from the message when displayed. For instance, given
    79  			//	>
    80  			//	>	flag.String("I", "", "search `directory` for include files")
    81  			//	>
    82  			//	> the output will be
    83  			//	>
    84  			// 	> 	-I directory
    85  			//	>		search directory for include files.
    86  			name, usage := goflag.UnquoteUsage(f)
    87  
    88  			// From the standard library documentation:
    89  			//	> For bool flags, the type is omitted and if the flag name is
    90  			//	> one byte the usage message appears on the same line.
    91  			if bf, ok := f.Value.(maybeBoolFlag); ok && bf.IsBoolFlag() && len(name) == 1 {
    92  				fmt.Fprintf(&buf, "-%s\t%s", f.Name, usage)
    93  				return
    94  			}
    95  
    96  			// First line: name, and, type or backticked name.
    97  			buf.WriteString("--")
    98  			buf.WriteString(f.Name)
    99  			if name != "" {
   100  				fmt.Fprintf(&buf, " %s", name)
   101  			}
   102  			buf.WriteString("\n\t")
   103  
   104  			// Second line: usage and optional default, if not the zero value
   105  			// for the type.
   106  			buf.WriteString(usage)
   107  			if !isZeroValue(f, f.DefValue) {
   108  				fmt.Fprintf(&buf, " (default %s)", f.DefValue)
   109  			}
   110  		})
   111  
   112  		if opts.Epilogue != nil {
   113  			opts.Epilogue(fs.Output())
   114  		}
   115  	}
   116  }