Optional long arguments
This commit is contained in:
parent
b91c03d2c6
commit
faebd3e0f2
16
parse.go
16
parse.go
|
@ -48,6 +48,7 @@ func (p path) Child(f reflect.StructField) path {
|
||||||
type spec struct {
|
type spec struct {
|
||||||
dest path
|
dest path
|
||||||
typ reflect.Type
|
typ reflect.Type
|
||||||
|
name string // canonical name for the option
|
||||||
long string
|
long string
|
||||||
short string
|
short string
|
||||||
multiple bool
|
multiple bool
|
||||||
|
@ -280,6 +281,8 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {
|
||||||
typ: field.Type,
|
typ: field.Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spec.name = spec.long
|
||||||
|
|
||||||
help, exists := field.Tag.Lookup("help")
|
help, exists := field.Tag.Lookup("help")
|
||||||
if exists {
|
if exists {
|
||||||
spec.help = help
|
spec.help = help
|
||||||
|
@ -308,6 +311,9 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {
|
||||||
errs = append(errs, fmt.Sprintf("%s.%s: too many hyphens", t.Name(), field.Name))
|
errs = append(errs, fmt.Sprintf("%s.%s: too many hyphens", t.Name(), field.Name))
|
||||||
case strings.HasPrefix(key, "--"):
|
case strings.HasPrefix(key, "--"):
|
||||||
spec.long = key[2:]
|
spec.long = key[2:]
|
||||||
|
if spec.long != "" {
|
||||||
|
spec.name = spec.long
|
||||||
|
}
|
||||||
case strings.HasPrefix(key, "-"):
|
case strings.HasPrefix(key, "-"):
|
||||||
if len(key) != 2 {
|
if len(key) != 2 {
|
||||||
errs = append(errs, fmt.Sprintf("%s.%s: short arguments must be one character only",
|
errs = append(errs, fmt.Sprintf("%s.%s: short arguments must be one character only",
|
||||||
|
@ -364,7 +370,7 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {
|
||||||
if hasPlaceholder {
|
if hasPlaceholder {
|
||||||
spec.placeholder = placeholder
|
spec.placeholder = placeholder
|
||||||
} else {
|
} else {
|
||||||
spec.placeholder = strings.ToUpper(spec.long)
|
spec.placeholder = strings.ToUpper(spec.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether this field is supported. It's good to do this here rather than
|
// Check whether this field is supported. It's good to do this here rather than
|
||||||
|
@ -617,13 +623,13 @@ func (p *Parser) process(args []string) error {
|
||||||
if spec.multiple {
|
if spec.multiple {
|
||||||
err := setSlice(p.val(spec.dest), positionals, true)
|
err := setSlice(p.val(spec.dest), positionals, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error processing %s: %v", spec.long, err)
|
return fmt.Errorf("error processing %s: %v", spec.name, err)
|
||||||
}
|
}
|
||||||
positionals = nil
|
positionals = nil
|
||||||
} else {
|
} else {
|
||||||
err := scalar.ParseValue(p.val(spec.dest), positionals[0])
|
err := scalar.ParseValue(p.val(spec.dest), positionals[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error processing %s: %v", spec.long, err)
|
return fmt.Errorf("error processing %s: %v", spec.name, err)
|
||||||
}
|
}
|
||||||
positionals = positionals[1:]
|
positionals = positionals[1:]
|
||||||
}
|
}
|
||||||
|
@ -638,8 +644,8 @@ func (p *Parser) process(args []string) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
name := spec.long
|
name := spec.name
|
||||||
if !spec.positional {
|
if spec.long != "" && !spec.positional {
|
||||||
name = "--" + spec.long
|
name = "--" + spec.long
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -231,6 +231,18 @@ func TestPlaceholder(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNoLongName(t *testing.T) {
|
||||||
|
var args struct {
|
||||||
|
ShortOnly string `arg:"-s,--"`
|
||||||
|
EnvOnly string `arg:"--,env"`
|
||||||
|
}
|
||||||
|
setenv(t, "ENVONLY", "TestVal")
|
||||||
|
err := parse("-s TestVal2", &args)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "TestVal", args.EnvOnly)
|
||||||
|
assert.Equal(t, "TestVal2", args.ShortOnly)
|
||||||
|
}
|
||||||
|
|
||||||
func TestCaseSensitive(t *testing.T) {
|
func TestCaseSensitive(t *testing.T) {
|
||||||
var args struct {
|
var args struct {
|
||||||
Lower bool `arg:"-v"`
|
Lower bool `arg:"-v"`
|
||||||
|
|
35
usage.go
35
usage.go
|
@ -36,12 +36,15 @@ func (p *Parser) WriteUsage(w io.Writer) {
|
||||||
|
|
||||||
// writeUsageForCommand writes usage information for the given subcommand
|
// writeUsageForCommand writes usage information for the given subcommand
|
||||||
func (p *Parser) writeUsageForCommand(w io.Writer, cmd *command) {
|
func (p *Parser) writeUsageForCommand(w io.Writer, cmd *command) {
|
||||||
var positionals, options []*spec
|
var positionals, longOptions, shortOptions []*spec
|
||||||
for _, spec := range cmd.specs {
|
for _, spec := range cmd.specs {
|
||||||
if spec.positional {
|
switch {
|
||||||
|
case spec.positional:
|
||||||
positionals = append(positionals, spec)
|
positionals = append(positionals, spec)
|
||||||
} else {
|
case spec.long != "":
|
||||||
options = append(options, spec)
|
longOptions = append(longOptions, spec)
|
||||||
|
case spec.short != "":
|
||||||
|
shortOptions = append(shortOptions, spec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +67,19 @@ func (p *Parser) writeUsageForCommand(w io.Writer, cmd *command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the option component of the usage message
|
// write the option component of the usage message
|
||||||
for _, spec := range options {
|
for _, spec := range shortOptions {
|
||||||
|
// prefix with a space
|
||||||
|
fmt.Fprint(w, " ")
|
||||||
|
if !spec.required {
|
||||||
|
fmt.Fprint(w, "[")
|
||||||
|
}
|
||||||
|
fmt.Fprint(w, synopsis(spec, "-"+spec.short))
|
||||||
|
if !spec.required {
|
||||||
|
fmt.Fprint(w, "]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, spec := range longOptions {
|
||||||
// prefix with a space
|
// prefix with a space
|
||||||
fmt.Fprint(w, " ")
|
fmt.Fprint(w, " ")
|
||||||
if !spec.required {
|
if !spec.required {
|
||||||
|
@ -215,10 +230,14 @@ func (p *Parser) writeHelpForCommand(w io.Writer, cmd *command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) printOption(w io.Writer, spec *spec) {
|
func (p *Parser) printOption(w io.Writer, spec *spec) {
|
||||||
left := synopsis(spec, "--"+spec.long)
|
text := make([]string, 0, 2)
|
||||||
if spec.short != "" {
|
if spec.long != "" {
|
||||||
left += ", " + synopsis(spec, "-"+spec.short)
|
text = append(text, synopsis(spec, "--"+spec.long))
|
||||||
}
|
}
|
||||||
|
if spec.short != "" {
|
||||||
|
text = append(text, synopsis(spec, "-"+spec.short))
|
||||||
|
}
|
||||||
|
left := strings.Join(text, ", ")
|
||||||
printTwoCols(w, left, spec.help, spec.defaultVal, spec.env)
|
printTwoCols(w, left, spec.help, spec.defaultVal, spec.env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -309,3 +309,22 @@ Global options:
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp, help.String())
|
assert.Equal(t, expectedHelp, help.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUsageWithOptionalLongNames(t *testing.T) {
|
||||||
|
expectedHelp := `Usage: example [-a PLACEHOLDER] -b SHORTONLY2
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-a PLACEHOLDER some help [default: some val]
|
||||||
|
-b SHORTONLY2 some help2
|
||||||
|
--help, -h display this help and exit
|
||||||
|
`
|
||||||
|
var args struct {
|
||||||
|
ShortOnly string `arg:"-a,--" help:"some help" default:"some val" placeholder:"PLACEHOLDER"`
|
||||||
|
ShortOnly2 string `arg:"-b,--,required" help:"some help2"`
|
||||||
|
}
|
||||||
|
p, err := NewParser(Config{Program: "example"}, &args)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
var help bytes.Buffer
|
||||||
|
p.WriteHelp(&help)
|
||||||
|
assert.Equal(t, expectedHelp, help.String())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue