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 }