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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
package arg
import (
"fmt"
"io"
"os"
"strings"
)
// the width of the left column
const colWidth = 25
// to allow monkey patching in tests
var stderr = os.Stderr
// Fail prints usage information to stderr and exits with non-zero status
func (p *Parser) Fail(msg string) {
p.failWithCommand(msg, p.cmd)
}
// failWithCommand prints usage information for the given subcommand to stderr and exits with non-zero status
func (p *Parser) failWithCommand(msg string, cmd *command) {
p.writeUsageForCommand(stderr, cmd)
fmt.Fprintln(stderr, "error:", msg)
osExit(-1)
}
// WriteUsage writes usage information to the given writer
func (p *Parser) WriteUsage(w io.Writer) {
p.writeUsageForCommand(w, p.cmd)
}
// writeUsageForCommand writes usage information for the given subcommand
func (p *Parser) writeUsageForCommand(w io.Writer, cmd *command) {
var positionals, options []*spec
for _, spec := range cmd.specs {
if spec.positional {
positionals = append(positionals, spec)
} else {
options = append(options, spec)
}
}
if p.version != "" {
fmt.Fprintln(w, p.version)
}
// make a list of ancestor commands so that we print with full context
var ancestors []string
ancestor := cmd
for ancestor != nil {
ancestors = append(ancestors, ancestor.name)
ancestor = ancestor.parent
}
// print the beginning of the usage string
fmt.Fprint(w, "Usage:")
for i := len(ancestors) - 1; i >= 0; i-- {
fmt.Fprint(w, " "+ancestors[i])
}
// write the option component of the usage message
for _, spec := range options {
// prefix with a space
fmt.Fprint(w, " ")
if !spec.required {
fmt.Fprint(w, "[")
}
fmt.Fprint(w, synopsis(spec, "--"+spec.long))
if !spec.required {
fmt.Fprint(w, "]")
}
}
// write the positional component of the usage message
for _, spec := range positionals {
// prefix with a space
fmt.Fprint(w, " ")
if spec.multiple {
if !spec.required {
fmt.Fprint(w, "[")
}
fmt.Fprintf(w, "%s [%s ...]", spec.placeholder, spec.placeholder)
if !spec.required {
fmt.Fprint(w, "]")
}
} else {
fmt.Fprint(w, spec.placeholder)
}
}
// if the program supports subcommands, give a hint to the user about their existence
if len(cmd.subcommands) > 0 {
fmt.Fprint(w, " <command> [<args>]")
}
fmt.Fprint(w, "\n")
}
func printTwoCols(w io.Writer, left, help string, defaultVal string) {
lhs := " " + left
fmt.Fprint(w, lhs)
if help != "" {
if len(lhs)+2 < colWidth {
fmt.Fprint(w, strings.Repeat(" ", colWidth-len(lhs)))
} else {
fmt.Fprint(w, "\n"+strings.Repeat(" ", colWidth))
}
fmt.Fprint(w, help)
}
if defaultVal != "" {
fmt.Fprintf(w, " [default: %s]", defaultVal)
}
fmt.Fprint(w, "\n")
}
// WriteHelp writes the usage string followed by the full help string for each option
func (p *Parser) WriteHelp(w io.Writer) {
p.writeHelpForCommand(w, p.cmd)
}
// writeHelp writes the usage string for the given subcommand
func (p *Parser) writeHelpForCommand(w io.Writer, cmd *command) {
var positionals, options []*spec
for _, spec := range cmd.specs {
if spec.positional {
positionals = append(positionals, spec)
} else {
options = append(options, spec)
}
}
if p.description != "" {
fmt.Fprintln(w, p.description)
}
p.writeUsageForCommand(w, cmd)
// write the list of positionals
if len(positionals) > 0 {
fmt.Fprint(w, "\nPositional arguments:\n")
for _, spec := range positionals {
printTwoCols(w, spec.placeholder, spec.help, "")
}
}
// write the list of options
fmt.Fprint(w, "\nOptions:\n")
for _, spec := range options {
p.printOption(w, spec)
}
// write the list of built in options
p.printOption(w, &spec{
boolean: true,
long: "help",
short: "h",
help: "display this help and exit",
})
if p.version != "" {
p.printOption(w, &spec{
boolean: true,
long: "version",
help: "display version and exit",
})
}
// write the list of subcommands
if len(cmd.subcommands) > 0 {
fmt.Fprint(w, "\nCommands:\n")
for _, subcmd := range cmd.subcommands {
printTwoCols(w, subcmd.name, subcmd.help, "")
}
}
}
func (p *Parser) printOption(w io.Writer, spec *spec) {
left := synopsis(spec, "--"+spec.long)
if spec.short != "" {
left += ", " + synopsis(spec, "-"+spec.short)
}
printTwoCols(w, left, spec.help, spec.defaultVal)
}
func synopsis(spec *spec, form string) string {
if spec.boolean {
return form
}
return form + " " + spec.placeholder
}
func ptrTo(s string) *string {
return &s
}
|