Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
Jeff Carr | 9b062eff85 |
23
Makefile
23
Makefile
|
@ -1,23 +1,10 @@
|
||||||
# git remote add gitwit git@git.wit.org:wit/arg.git
|
all:
|
||||||
|
@echo
|
||||||
|
@echo "this is from alexflint's github repo"
|
||||||
|
@echo "the 'github' branch is the upstream branch"
|
||||||
|
@echo
|
||||||
|
|
||||||
redomod:
|
redomod:
|
||||||
rm -f go.*
|
rm -f go.*
|
||||||
GO111MODULE= go mod init
|
GO111MODULE= go mod init
|
||||||
GO111MODULE= go mod tidy
|
GO111MODULE= go mod tidy
|
||||||
|
|
||||||
github:
|
|
||||||
git push origin register
|
|
||||||
git push origin devel
|
|
||||||
git push origin jcarr
|
|
||||||
git push origin --tags
|
|
||||||
# git push github register
|
|
||||||
# git push github devel
|
|
||||||
# git push github --tags
|
|
||||||
@echo
|
|
||||||
@echo check https://git.wit.org/wit/arg
|
|
||||||
@echo
|
|
||||||
|
|
||||||
# init-github:
|
|
||||||
# git push -u github master
|
|
||||||
# git push -u github devel
|
|
||||||
# git push github --tags
|
|
||||||
|
|
540
example_test.go
540
example_test.go
|
@ -1,540 +0,0 @@
|
||||||
package arg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/mail"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func split(s string) []string {
|
|
||||||
return strings.Split(s, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates basic usage
|
|
||||||
func Example() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example --foo=hello --bar")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Foo string
|
|
||||||
Bar bool
|
|
||||||
}
|
|
||||||
MustParse(&args)
|
|
||||||
fmt.Println(args.Foo, args.Bar)
|
|
||||||
// output: hello true
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates arguments that have default values
|
|
||||||
func Example_defaultValues() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Foo string `default:"abc"`
|
|
||||||
}
|
|
||||||
MustParse(&args)
|
|
||||||
fmt.Println(args.Foo)
|
|
||||||
// output: abc
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates arguments that are required
|
|
||||||
func Example_requiredArguments() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example --foo=abc --bar")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Foo string `arg:"required"`
|
|
||||||
Bar bool
|
|
||||||
}
|
|
||||||
MustParse(&args)
|
|
||||||
fmt.Println(args.Foo, args.Bar)
|
|
||||||
// output: abc true
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates positional arguments
|
|
||||||
func Example_positionalArguments() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example in out1 out2 out3")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Input string `arg:"positional"`
|
|
||||||
Output []string `arg:"positional"`
|
|
||||||
}
|
|
||||||
MustParse(&args)
|
|
||||||
fmt.Println("In:", args.Input)
|
|
||||||
fmt.Println("Out:", args.Output)
|
|
||||||
// output:
|
|
||||||
// In: in
|
|
||||||
// Out: [out1 out2 out3]
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates arguments that have multiple values
|
|
||||||
func Example_multipleValues() {
|
|
||||||
// The args you would pass in on the command line
|
|
||||||
os.Args = split("./example --database localhost --ids 1 2 3")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Database string
|
|
||||||
IDs []int64
|
|
||||||
}
|
|
||||||
MustParse(&args)
|
|
||||||
fmt.Printf("Fetching the following IDs from %s: %v", args.Database, args.IDs)
|
|
||||||
// output: Fetching the following IDs from localhost: [1 2 3]
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates arguments with keys and values
|
|
||||||
func Example_mappings() {
|
|
||||||
// The args you would pass in on the command line
|
|
||||||
os.Args = split("./example --userids john=123 mary=456")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
UserIDs map[string]int
|
|
||||||
}
|
|
||||||
MustParse(&args)
|
|
||||||
fmt.Println(args.UserIDs)
|
|
||||||
// output: map[john:123 mary:456]
|
|
||||||
}
|
|
||||||
|
|
||||||
type commaSeparated struct {
|
|
||||||
M map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *commaSeparated) UnmarshalText(b []byte) error {
|
|
||||||
c.M = make(map[string]string)
|
|
||||||
for _, part := range strings.Split(string(b), ",") {
|
|
||||||
pos := strings.Index(part, "=")
|
|
||||||
if pos == -1 {
|
|
||||||
return fmt.Errorf("error parsing %q, expected format key=value", part)
|
|
||||||
}
|
|
||||||
c.M[part[:pos]] = part[pos+1:]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates arguments with keys and values separated by commas
|
|
||||||
func Example_mappingWithCommas() {
|
|
||||||
// The args you would pass in on the command line
|
|
||||||
os.Args = split("./example --values one=two,three=four")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Values commaSeparated
|
|
||||||
}
|
|
||||||
MustParse(&args)
|
|
||||||
fmt.Println(args.Values.M)
|
|
||||||
// output: map[one:two three:four]
|
|
||||||
}
|
|
||||||
|
|
||||||
// This eample demonstrates multiple value arguments that can be mixed with
|
|
||||||
// other arguments.
|
|
||||||
func Example_multipleMixed() {
|
|
||||||
os.Args = split("./example -c cmd1 db1 -f file1 db2 -c cmd2 -f file2 -f file3 db3 -c cmd3")
|
|
||||||
var args struct {
|
|
||||||
Commands []string `arg:"-c,separate"`
|
|
||||||
Files []string `arg:"-f,separate"`
|
|
||||||
Databases []string `arg:"positional"`
|
|
||||||
}
|
|
||||||
MustParse(&args)
|
|
||||||
fmt.Println("Commands:", args.Commands)
|
|
||||||
fmt.Println("Files:", args.Files)
|
|
||||||
fmt.Println("Databases:", args.Databases)
|
|
||||||
|
|
||||||
// output:
|
|
||||||
// Commands: [cmd1 cmd2 cmd3]
|
|
||||||
// Files: [file1 file2 file3]
|
|
||||||
// Databases: [db1 db2 db3]
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows the usage string generated by go-arg
|
|
||||||
func Example_helpText() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example --help")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Input string `arg:"positional,required"`
|
|
||||||
Output []string `arg:"positional"`
|
|
||||||
Verbose bool `arg:"-v" help:"verbosity level"`
|
|
||||||
Dataset string `help:"dataset to use"`
|
|
||||||
Optimize int `arg:"-O,--optim" help:"optimization level"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
|
||||||
mustParseExit = func(int) {}
|
|
||||||
|
|
||||||
MustParse(&args)
|
|
||||||
|
|
||||||
// output:
|
|
||||||
// Usage: example [--verbose] [--dataset DATASET] [--optim OPTIM] INPUT [OUTPUT [OUTPUT ...]]
|
|
||||||
//
|
|
||||||
// Positional arguments:
|
|
||||||
// INPUT
|
|
||||||
// OUTPUT
|
|
||||||
//
|
|
||||||
// Options:
|
|
||||||
// --verbose, -v verbosity level
|
|
||||||
// --dataset DATASET dataset to use
|
|
||||||
// --optim OPTIM, -O OPTIM
|
|
||||||
// optimization level
|
|
||||||
// --help, -h display this help and exit
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows the usage string generated by go-arg with customized placeholders
|
|
||||||
func Example_helpPlaceholder() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example --help")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Input string `arg:"positional,required" placeholder:"SRC"`
|
|
||||||
Output []string `arg:"positional" placeholder:"DST"`
|
|
||||||
Optimize int `arg:"-O" help:"optimization level" placeholder:"LEVEL"`
|
|
||||||
MaxJobs int `arg:"-j" help:"maximum number of simultaneous jobs" placeholder:"N"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
|
||||||
mustParseExit = func(int) {}
|
|
||||||
|
|
||||||
MustParse(&args)
|
|
||||||
|
|
||||||
// output:
|
|
||||||
|
|
||||||
// Usage: example [--optimize LEVEL] [--maxjobs N] SRC [DST [DST ...]]
|
|
||||||
|
|
||||||
// Positional arguments:
|
|
||||||
// SRC
|
|
||||||
// DST
|
|
||||||
|
|
||||||
// Options:
|
|
||||||
// --optimize LEVEL, -O LEVEL
|
|
||||||
// optimization level
|
|
||||||
// --maxjobs N, -j N maximum number of simultaneous jobs
|
|
||||||
// --help, -h display this help and exit
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows the usage string generated by go-arg when using subcommands
|
|
||||||
func Example_helpTextWithSubcommand() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example --help")
|
|
||||||
|
|
||||||
type getCmd struct {
|
|
||||||
Item string `arg:"positional" help:"item to fetch"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type listCmd struct {
|
|
||||||
Format string `help:"output format"`
|
|
||||||
Limit int
|
|
||||||
}
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Verbose bool
|
|
||||||
Get *getCmd `arg:"subcommand" help:"fetch an item and print it"`
|
|
||||||
List *listCmd `arg:"subcommand" help:"list available items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
|
||||||
mustParseExit = func(int) {}
|
|
||||||
|
|
||||||
MustParse(&args)
|
|
||||||
|
|
||||||
// output:
|
|
||||||
// Usage: example [--verbose] <command> [<args>]
|
|
||||||
//
|
|
||||||
// Options:
|
|
||||||
// --verbose
|
|
||||||
// --help, -h display this help and exit
|
|
||||||
//
|
|
||||||
// Commands:
|
|
||||||
// get fetch an item and print it
|
|
||||||
// list list available items
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows the usage string generated by go-arg when using subcommands
|
|
||||||
func Example_helpTextWhenUsingSubcommand() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example get --help")
|
|
||||||
|
|
||||||
type getCmd struct {
|
|
||||||
Item string `arg:"positional,required" help:"item to fetch"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type listCmd struct {
|
|
||||||
Format string `help:"output format"`
|
|
||||||
Limit int
|
|
||||||
}
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Verbose bool
|
|
||||||
Get *getCmd `arg:"subcommand" help:"fetch an item and print it"`
|
|
||||||
List *listCmd `arg:"subcommand" help:"list available items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
|
||||||
mustParseExit = func(int) {}
|
|
||||||
|
|
||||||
MustParse(&args)
|
|
||||||
|
|
||||||
// output:
|
|
||||||
// Usage: example get ITEM
|
|
||||||
//
|
|
||||||
// Positional arguments:
|
|
||||||
// ITEM item to fetch
|
|
||||||
//
|
|
||||||
// Global options:
|
|
||||||
// --verbose
|
|
||||||
// --help, -h display this help and exit
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to print help for an explicit subcommand
|
|
||||||
func Example_writeHelpForSubcommand() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example get --help")
|
|
||||||
|
|
||||||
type getCmd struct {
|
|
||||||
Item string `arg:"positional" help:"item to fetch"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type listCmd struct {
|
|
||||||
Format string `help:"output format"`
|
|
||||||
Limit int
|
|
||||||
}
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Verbose bool
|
|
||||||
Get *getCmd `arg:"subcommand" help:"fetch an item and print it"`
|
|
||||||
List *listCmd `arg:"subcommand" help:"list available items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
|
||||||
exit := func(int) {}
|
|
||||||
|
|
||||||
p, err := NewParser(Config{Exit: exit}, &args)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = p.WriteHelpForSubcommand(os.Stdout, "list")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// output:
|
|
||||||
// Usage: example list [--format FORMAT] [--limit LIMIT]
|
|
||||||
//
|
|
||||||
// Options:
|
|
||||||
// --format FORMAT output format
|
|
||||||
// --limit LIMIT
|
|
||||||
//
|
|
||||||
// Global options:
|
|
||||||
// --verbose
|
|
||||||
// --help, -h display this help and exit
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows how to print help for a subcommand that is nested several levels deep
|
|
||||||
func Example_writeHelpForSubcommandNested() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example get --help")
|
|
||||||
|
|
||||||
type mostNestedCmd struct {
|
|
||||||
Item string
|
|
||||||
}
|
|
||||||
|
|
||||||
type nestedCmd struct {
|
|
||||||
MostNested *mostNestedCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type topLevelCmd struct {
|
|
||||||
Nested *nestedCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
TopLevel *topLevelCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
|
||||||
exit := func(int) {}
|
|
||||||
|
|
||||||
p, err := NewParser(Config{Exit: exit}, &args)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = p.WriteHelpForSubcommand(os.Stdout, "toplevel", "nested", "mostnested")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// output:
|
|
||||||
// Usage: example toplevel nested mostnested [--item ITEM]
|
|
||||||
//
|
|
||||||
// Options:
|
|
||||||
// --item ITEM
|
|
||||||
// --help, -h display this help and exit
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows the error string generated by go-arg when an invalid option is provided
|
|
||||||
func Example_errorText() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example --optimize INVALID")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Input string `arg:"positional,required"`
|
|
||||||
Output []string `arg:"positional"`
|
|
||||||
Verbose bool `arg:"-v" help:"verbosity level"`
|
|
||||||
Dataset string `help:"dataset to use"`
|
|
||||||
Optimize int `arg:"-O,help:optimization level"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
|
||||||
mustParseExit = func(int) {}
|
|
||||||
|
|
||||||
MustParse(&args)
|
|
||||||
|
|
||||||
// output:
|
|
||||||
// Usage: example [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] INPUT [OUTPUT [OUTPUT ...]]
|
|
||||||
// error: error processing --optimize: strconv.ParseInt: parsing "INVALID": invalid syntax
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example shows the error string generated by go-arg when an invalid option is provided
|
|
||||||
func Example_errorTextForSubcommand() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example get --count INVALID")
|
|
||||||
|
|
||||||
type getCmd struct {
|
|
||||||
Count int
|
|
||||||
}
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Get *getCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
|
||||||
mustParseExit = func(int) {}
|
|
||||||
|
|
||||||
MustParse(&args)
|
|
||||||
|
|
||||||
// output:
|
|
||||||
// Usage: example get [--count COUNT]
|
|
||||||
// error: error processing --count: strconv.ParseInt: parsing "INVALID": invalid syntax
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates use of subcommands
|
|
||||||
func Example_subcommand() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = split("./example commit -a -m what-this-commit-is-about")
|
|
||||||
|
|
||||||
type CheckoutCmd struct {
|
|
||||||
Branch string `arg:"positional"`
|
|
||||||
Track bool `arg:"-t"`
|
|
||||||
}
|
|
||||||
type CommitCmd struct {
|
|
||||||
All bool `arg:"-a"`
|
|
||||||
Message string `arg:"-m"`
|
|
||||||
}
|
|
||||||
type PushCmd struct {
|
|
||||||
Remote string `arg:"positional"`
|
|
||||||
Branch string `arg:"positional"`
|
|
||||||
SetUpstream bool `arg:"-u"`
|
|
||||||
}
|
|
||||||
var args struct {
|
|
||||||
Checkout *CheckoutCmd `arg:"subcommand:checkout"`
|
|
||||||
Commit *CommitCmd `arg:"subcommand:commit"`
|
|
||||||
Push *PushCmd `arg:"subcommand:push"`
|
|
||||||
Quiet bool `arg:"-q"` // this flag is global to all subcommands
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
|
||||||
mustParseExit = func(int) {}
|
|
||||||
|
|
||||||
MustParse(&args)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case args.Checkout != nil:
|
|
||||||
fmt.Printf("checkout requested for branch %s\n", args.Checkout.Branch)
|
|
||||||
case args.Commit != nil:
|
|
||||||
fmt.Printf("commit requested with message \"%s\"\n", args.Commit.Message)
|
|
||||||
case args.Push != nil:
|
|
||||||
fmt.Printf("push requested from %s to %s\n", args.Push.Branch, args.Push.Remote)
|
|
||||||
}
|
|
||||||
|
|
||||||
// output:
|
|
||||||
// commit requested with message "what-this-commit-is-about"
|
|
||||||
}
|
|
||||||
|
|
||||||
func Example_allSupportedTypes() {
|
|
||||||
// These are the args you would pass in on the command line
|
|
||||||
os.Args = []string{}
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Bool bool
|
|
||||||
Byte byte
|
|
||||||
Rune rune
|
|
||||||
Int int
|
|
||||||
Int8 int8
|
|
||||||
Int16 int16
|
|
||||||
Int32 int32
|
|
||||||
Int64 int64
|
|
||||||
Float32 float32
|
|
||||||
Float64 float64
|
|
||||||
String string
|
|
||||||
Duration time.Duration
|
|
||||||
URL url.URL
|
|
||||||
Email mail.Address
|
|
||||||
MAC net.HardwareAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
// go-arg supports each of the types above, as well as pointers to any of
|
|
||||||
// the above and slices of any of the above. It also supports any types that
|
|
||||||
// implements encoding.TextUnmarshaler.
|
|
||||||
|
|
||||||
MustParse(&args)
|
|
||||||
|
|
||||||
// output:
|
|
||||||
}
|
|
||||||
|
|
||||||
func Example_envVarOnly() {
|
|
||||||
os.Args = split("./example")
|
|
||||||
_ = os.Setenv("AUTH_KEY", "my_key")
|
|
||||||
|
|
||||||
defer os.Unsetenv("AUTH_KEY")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
AuthKey string `arg:"--,env:AUTH_KEY"`
|
|
||||||
}
|
|
||||||
|
|
||||||
MustParse(&args)
|
|
||||||
|
|
||||||
fmt.Println(args.AuthKey)
|
|
||||||
// output: my_key
|
|
||||||
}
|
|
||||||
|
|
||||||
func Example_envVarOnlyShouldIgnoreFlag() {
|
|
||||||
os.Args = split("./example --=my_key")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
AuthKey string `arg:"--,env:AUTH_KEY"`
|
|
||||||
}
|
|
||||||
|
|
||||||
err := Parse(&args)
|
|
||||||
|
|
||||||
fmt.Println(err)
|
|
||||||
// output: unknown argument --=my_key
|
|
||||||
}
|
|
||||||
|
|
||||||
func Example_envVarOnlyShouldIgnoreShortFlag() {
|
|
||||||
os.Args = split("./example -=my_key")
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
AuthKey string `arg:"--,env:AUTH_KEY"`
|
|
||||||
}
|
|
||||||
|
|
||||||
err := Parse(&args)
|
|
||||||
|
|
||||||
fmt.Println(err)
|
|
||||||
// output: unknown argument -=my_key
|
|
||||||
}
|
|
11
go.mod
11
go.mod
|
@ -2,13 +2,4 @@ module go.wit.com/dev/alexflint/go-arg
|
||||||
|
|
||||||
go 1.21.4
|
go 1.21.4
|
||||||
|
|
||||||
require (
|
require github.com/alexflint/go-scalar v1.2.0
|
||||||
github.com/alexflint/go-scalar v1.2.0
|
|
||||||
github.com/stretchr/testify v1.8.4
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
|
||||||
|
|
7
go.sum
7
go.sum
|
@ -4,10 +4,5 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
|
|
1739
parse_test.go
1739
parse_test.go
File diff suppressed because it is too large
Load Diff
112
reflect_test.go
112
reflect_test.go
|
@ -1,112 +0,0 @@
|
||||||
package arg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func assertCardinality(t *testing.T, typ reflect.Type, expected cardinality) {
|
|
||||||
actual, err := cardinalityOf(typ)
|
|
||||||
assert.Equal(t, expected, actual, "expected %v to have cardinality %v but got %v", typ, expected, actual)
|
|
||||||
if expected == unsupported {
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCardinalityOf(t *testing.T) {
|
|
||||||
var b bool
|
|
||||||
var i int
|
|
||||||
var s string
|
|
||||||
var f float64
|
|
||||||
var bs []bool
|
|
||||||
var is []int
|
|
||||||
var m map[string]int
|
|
||||||
var unsupported1 struct{}
|
|
||||||
var unsupported2 []struct{}
|
|
||||||
var unsupported3 map[string]struct{}
|
|
||||||
var unsupported4 map[struct{}]string
|
|
||||||
|
|
||||||
assertCardinality(t, reflect.TypeOf(b), zero)
|
|
||||||
assertCardinality(t, reflect.TypeOf(i), one)
|
|
||||||
assertCardinality(t, reflect.TypeOf(s), one)
|
|
||||||
assertCardinality(t, reflect.TypeOf(f), one)
|
|
||||||
|
|
||||||
assertCardinality(t, reflect.TypeOf(&b), zero)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&s), one)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&i), one)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&f), one)
|
|
||||||
|
|
||||||
assertCardinality(t, reflect.TypeOf(bs), multiple)
|
|
||||||
assertCardinality(t, reflect.TypeOf(is), multiple)
|
|
||||||
|
|
||||||
assertCardinality(t, reflect.TypeOf(&bs), multiple)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&is), multiple)
|
|
||||||
|
|
||||||
assertCardinality(t, reflect.TypeOf(m), multiple)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&m), multiple)
|
|
||||||
|
|
||||||
assertCardinality(t, reflect.TypeOf(unsupported1), unsupported)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&unsupported1), unsupported)
|
|
||||||
assertCardinality(t, reflect.TypeOf(unsupported2), unsupported)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&unsupported2), unsupported)
|
|
||||||
assertCardinality(t, reflect.TypeOf(unsupported3), unsupported)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&unsupported3), unsupported)
|
|
||||||
assertCardinality(t, reflect.TypeOf(unsupported4), unsupported)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&unsupported4), unsupported)
|
|
||||||
}
|
|
||||||
|
|
||||||
type implementsTextUnmarshaler struct{}
|
|
||||||
|
|
||||||
func (*implementsTextUnmarshaler) UnmarshalText(text []byte) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCardinalityTextUnmarshaler(t *testing.T) {
|
|
||||||
var x implementsTextUnmarshaler
|
|
||||||
var s []implementsTextUnmarshaler
|
|
||||||
var m []implementsTextUnmarshaler
|
|
||||||
assertCardinality(t, reflect.TypeOf(x), one)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&x), one)
|
|
||||||
assertCardinality(t, reflect.TypeOf(s), multiple)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&s), multiple)
|
|
||||||
assertCardinality(t, reflect.TypeOf(m), multiple)
|
|
||||||
assertCardinality(t, reflect.TypeOf(&m), multiple)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsExported(t *testing.T) {
|
|
||||||
assert.True(t, isExported("Exported"))
|
|
||||||
assert.False(t, isExported("notExported"))
|
|
||||||
assert.False(t, isExported(""))
|
|
||||||
assert.False(t, isExported(string([]byte{255})))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCardinalityString(t *testing.T) {
|
|
||||||
assert.Equal(t, "zero", zero.String())
|
|
||||||
assert.Equal(t, "one", one.String())
|
|
||||||
assert.Equal(t, "multiple", multiple.String())
|
|
||||||
assert.Equal(t, "unsupported", unsupported.String())
|
|
||||||
assert.Equal(t, "unknown(42)", cardinality(42).String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsZero(t *testing.T) {
|
|
||||||
var zero int
|
|
||||||
var notZero = 3
|
|
||||||
var nilSlice []int
|
|
||||||
var nonNilSlice = []int{1, 2, 3}
|
|
||||||
var nilMap map[string]string
|
|
||||||
var nonNilMap = map[string]string{"foo": "bar"}
|
|
||||||
var uncomparable = func() {}
|
|
||||||
|
|
||||||
assert.True(t, isZero(reflect.ValueOf(zero)))
|
|
||||||
assert.False(t, isZero(reflect.ValueOf(notZero)))
|
|
||||||
|
|
||||||
assert.True(t, isZero(reflect.ValueOf(nilSlice)))
|
|
||||||
assert.False(t, isZero(reflect.ValueOf(nonNilSlice)))
|
|
||||||
|
|
||||||
assert.True(t, isZero(reflect.ValueOf(nilMap)))
|
|
||||||
assert.False(t, isZero(reflect.ValueOf(nonNilMap)))
|
|
||||||
|
|
||||||
assert.False(t, isZero(reflect.ValueOf(uncomparable)))
|
|
||||||
}
|
|
152
sequence_test.go
152
sequence_test.go
|
@ -1,152 +0,0 @@
|
||||||
package arg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSetSliceWithoutClearing(t *testing.T) {
|
|
||||||
xs := []int{10}
|
|
||||||
entries := []string{"1", "2", "3"}
|
|
||||||
err := setSlice(reflect.ValueOf(&xs).Elem(), entries, false)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, []int{10, 1, 2, 3}, xs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetSliceAfterClearing(t *testing.T) {
|
|
||||||
xs := []int{100}
|
|
||||||
entries := []string{"1", "2", "3"}
|
|
||||||
err := setSlice(reflect.ValueOf(&xs).Elem(), entries, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, []int{1, 2, 3}, xs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetSliceInvalid(t *testing.T) {
|
|
||||||
xs := []int{100}
|
|
||||||
entries := []string{"invalid"}
|
|
||||||
err := setSlice(reflect.ValueOf(&xs).Elem(), entries, true)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetSlicePtr(t *testing.T) {
|
|
||||||
var xs []*int
|
|
||||||
entries := []string{"1", "2", "3"}
|
|
||||||
err := setSlice(reflect.ValueOf(&xs).Elem(), entries, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, xs, 3)
|
|
||||||
assert.Equal(t, 1, *xs[0])
|
|
||||||
assert.Equal(t, 2, *xs[1])
|
|
||||||
assert.Equal(t, 3, *xs[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetSliceTextUnmarshaller(t *testing.T) {
|
|
||||||
// textUnmarshaler is a struct that captures the length of the string passed to it
|
|
||||||
var xs []*textUnmarshaler
|
|
||||||
entries := []string{"a", "aa", "aaa"}
|
|
||||||
err := setSlice(reflect.ValueOf(&xs).Elem(), entries, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, xs, 3)
|
|
||||||
assert.Equal(t, 1, xs[0].val)
|
|
||||||
assert.Equal(t, 2, xs[1].val)
|
|
||||||
assert.Equal(t, 3, xs[2].val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetMapWithoutClearing(t *testing.T) {
|
|
||||||
m := map[string]int{"foo": 10}
|
|
||||||
entries := []string{"a=1", "b=2"}
|
|
||||||
err := setMap(reflect.ValueOf(&m).Elem(), entries, false)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, m, 3)
|
|
||||||
assert.Equal(t, 1, m["a"])
|
|
||||||
assert.Equal(t, 2, m["b"])
|
|
||||||
assert.Equal(t, 10, m["foo"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetMapAfterClearing(t *testing.T) {
|
|
||||||
m := map[string]int{"foo": 10}
|
|
||||||
entries := []string{"a=1", "b=2"}
|
|
||||||
err := setMap(reflect.ValueOf(&m).Elem(), entries, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, m, 2)
|
|
||||||
assert.Equal(t, 1, m["a"])
|
|
||||||
assert.Equal(t, 2, m["b"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetMapWithKeyPointer(t *testing.T) {
|
|
||||||
// textUnmarshaler is a struct that captures the length of the string passed to it
|
|
||||||
var m map[*string]int
|
|
||||||
entries := []string{"abc=123"}
|
|
||||||
err := setMap(reflect.ValueOf(&m).Elem(), entries, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, m, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetMapWithValuePointer(t *testing.T) {
|
|
||||||
// textUnmarshaler is a struct that captures the length of the string passed to it
|
|
||||||
var m map[string]*int
|
|
||||||
entries := []string{"abc=123"}
|
|
||||||
err := setMap(reflect.ValueOf(&m).Elem(), entries, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, m, 1)
|
|
||||||
assert.Equal(t, 123, *m["abc"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetMapTextUnmarshaller(t *testing.T) {
|
|
||||||
// textUnmarshaler is a struct that captures the length of the string passed to it
|
|
||||||
var m map[textUnmarshaler]*textUnmarshaler
|
|
||||||
entries := []string{"a=123", "aa=12", "aaa=1"}
|
|
||||||
err := setMap(reflect.ValueOf(&m).Elem(), entries, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, m, 3)
|
|
||||||
assert.Equal(t, &textUnmarshaler{3}, m[textUnmarshaler{1}])
|
|
||||||
assert.Equal(t, &textUnmarshaler{2}, m[textUnmarshaler{2}])
|
|
||||||
assert.Equal(t, &textUnmarshaler{1}, m[textUnmarshaler{3}])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetMapInvalidKey(t *testing.T) {
|
|
||||||
var m map[int]int
|
|
||||||
entries := []string{"invalid=123"}
|
|
||||||
err := setMap(reflect.ValueOf(&m).Elem(), entries, true)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetMapInvalidValue(t *testing.T) {
|
|
||||||
var m map[int]int
|
|
||||||
entries := []string{"123=invalid"}
|
|
||||||
err := setMap(reflect.ValueOf(&m).Elem(), entries, true)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetMapMalformed(t *testing.T) {
|
|
||||||
// textUnmarshaler is a struct that captures the length of the string passed to it
|
|
||||||
var m map[string]string
|
|
||||||
entries := []string{"missing_equals_sign"}
|
|
||||||
err := setMap(reflect.ValueOf(&m).Elem(), entries, true)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetSliceOrMapErrors(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
var dest reflect.Value
|
|
||||||
|
|
||||||
// converting a slice to a reflect.Value in this way will make it read only
|
|
||||||
var cannotSet []int
|
|
||||||
dest = reflect.ValueOf(cannotSet)
|
|
||||||
err = setSliceOrMap(dest, nil, false)
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
// check what happens when we pass in something that is not a slice or a map
|
|
||||||
var notSliceOrMap string
|
|
||||||
dest = reflect.ValueOf(¬SliceOrMap).Elem()
|
|
||||||
err = setSliceOrMap(dest, nil, false)
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
// check what happens when we pass in a pointer to something that is not a slice or a map
|
|
||||||
var stringPtr *string
|
|
||||||
dest = reflect.ValueOf(&stringPtr).Elem()
|
|
||||||
err = setSliceOrMap(dest, nil, false)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
|
@ -1,508 +0,0 @@
|
||||||
package arg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This file contains tests for parse.go but I decided to put them here
|
|
||||||
// since that file is getting large
|
|
||||||
|
|
||||||
func TestSubcommandNotAPointer(t *testing.T) {
|
|
||||||
var args struct {
|
|
||||||
A string `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
_, err := NewParser(Config{}, &args)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubcommandNotAPointerToStruct(t *testing.T) {
|
|
||||||
var args struct {
|
|
||||||
A struct{} `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
_, err := NewParser(Config{}, &args)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPositionalAndSubcommandNotAllowed(t *testing.T) {
|
|
||||||
var args struct {
|
|
||||||
A string `arg:"positional"`
|
|
||||||
B *struct{} `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
_, err := NewParser(Config{}, &args)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMinimalSubcommand(t *testing.T) {
|
|
||||||
type listCmd struct {
|
|
||||||
}
|
|
||||||
var args struct {
|
|
||||||
List *listCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
p, err := pparse("list", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, args.List, p.Subcommand())
|
|
||||||
assert.Equal(t, []string{"list"}, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubcommandNamesBeforeParsing(t *testing.T) {
|
|
||||||
type listCmd struct{}
|
|
||||||
var args struct {
|
|
||||||
List *listCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
p, err := NewParser(Config{}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Nil(t, p.Subcommand())
|
|
||||||
assert.Nil(t, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoSuchSubcommand(t *testing.T) {
|
|
||||||
type listCmd struct {
|
|
||||||
}
|
|
||||||
var args struct {
|
|
||||||
List *listCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
_, err := pparse("invalid", &args)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNamedSubcommand(t *testing.T) {
|
|
||||||
type listCmd struct {
|
|
||||||
}
|
|
||||||
var args struct {
|
|
||||||
List *listCmd `arg:"subcommand:ls"`
|
|
||||||
}
|
|
||||||
p, err := pparse("ls", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, args.List, p.Subcommand())
|
|
||||||
assert.Equal(t, []string{"ls"}, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubcommandAliases(t *testing.T) {
|
|
||||||
type listCmd struct {
|
|
||||||
}
|
|
||||||
var args struct {
|
|
||||||
List *listCmd `arg:"subcommand:list|ls"`
|
|
||||||
}
|
|
||||||
p, err := pparse("ls", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, args.List, p.Subcommand())
|
|
||||||
assert.Equal(t, []string{"ls"}, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptySubcommand(t *testing.T) {
|
|
||||||
type listCmd struct {
|
|
||||||
}
|
|
||||||
var args struct {
|
|
||||||
List *listCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
p, err := pparse("", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Nil(t, args.List)
|
|
||||||
assert.Nil(t, p.Subcommand())
|
|
||||||
assert.Empty(t, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTwoSubcommands(t *testing.T) {
|
|
||||||
type getCmd struct {
|
|
||||||
}
|
|
||||||
type listCmd struct {
|
|
||||||
}
|
|
||||||
var args struct {
|
|
||||||
Get *getCmd `arg:"subcommand"`
|
|
||||||
List *listCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
p, err := pparse("list", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Nil(t, args.Get)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, args.List, p.Subcommand())
|
|
||||||
assert.Equal(t, []string{"list"}, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTwoSubcommandsWithAliases(t *testing.T) {
|
|
||||||
type getCmd struct {
|
|
||||||
}
|
|
||||||
type listCmd struct {
|
|
||||||
}
|
|
||||||
var args struct {
|
|
||||||
Get *getCmd `arg:"subcommand:get|g"`
|
|
||||||
List *listCmd `arg:"subcommand:list|ls"`
|
|
||||||
}
|
|
||||||
p, err := pparse("ls", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Nil(t, args.Get)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, args.List, p.Subcommand())
|
|
||||||
assert.Equal(t, []string{"ls"}, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubcommandsWithOptions(t *testing.T) {
|
|
||||||
type getCmd struct {
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
type listCmd struct {
|
|
||||||
Limit int
|
|
||||||
}
|
|
||||||
type cmd struct {
|
|
||||||
Verbose bool
|
|
||||||
Get *getCmd `arg:"subcommand"`
|
|
||||||
List *listCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("list", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Nil(t, args.Get)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("list --limit 3", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Nil(t, args.Get)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, args.List.Limit, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("list --limit 3 --verbose", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Nil(t, args.Get)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, args.List.Limit, 3)
|
|
||||||
assert.True(t, args.Verbose)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("list --verbose --limit 3", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Nil(t, args.Get)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, args.List.Limit, 3)
|
|
||||||
assert.True(t, args.Verbose)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("--verbose list --limit 3", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Nil(t, args.Get)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, args.List.Limit, 3)
|
|
||||||
assert.True(t, args.Verbose)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("get", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.Get)
|
|
||||||
assert.Nil(t, args.List)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("get --name test", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.Get)
|
|
||||||
assert.Nil(t, args.List)
|
|
||||||
assert.Equal(t, args.Get.Name, "test")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubcommandsWithEnvVars(t *testing.T) {
|
|
||||||
type getCmd struct {
|
|
||||||
Name string `arg:"env"`
|
|
||||||
}
|
|
||||||
type listCmd struct {
|
|
||||||
Limit int `arg:"env"`
|
|
||||||
}
|
|
||||||
type cmd struct {
|
|
||||||
Verbose bool
|
|
||||||
Get *getCmd `arg:"subcommand"`
|
|
||||||
List *listCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
setenv(t, "LIMIT", "123")
|
|
||||||
err := parse("list", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, 123, args.List.Limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
setenv(t, "LIMIT", "not_an_integer")
|
|
||||||
err := parse("list", &args)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNestedSubcommands(t *testing.T) {
|
|
||||||
type child struct{}
|
|
||||||
type parent struct {
|
|
||||||
Child *child `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
type grandparent struct {
|
|
||||||
Parent *parent `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
type root struct {
|
|
||||||
Grandparent *grandparent `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args root
|
|
||||||
p, err := pparse("grandparent parent child", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, args.Grandparent)
|
|
||||||
require.NotNil(t, args.Grandparent.Parent)
|
|
||||||
require.NotNil(t, args.Grandparent.Parent.Child)
|
|
||||||
assert.Equal(t, args.Grandparent.Parent.Child, p.Subcommand())
|
|
||||||
assert.Equal(t, []string{"grandparent", "parent", "child"}, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args root
|
|
||||||
p, err := pparse("grandparent parent", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, args.Grandparent)
|
|
||||||
require.NotNil(t, args.Grandparent.Parent)
|
|
||||||
require.Nil(t, args.Grandparent.Parent.Child)
|
|
||||||
assert.Equal(t, args.Grandparent.Parent, p.Subcommand())
|
|
||||||
assert.Equal(t, []string{"grandparent", "parent"}, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args root
|
|
||||||
p, err := pparse("grandparent", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, args.Grandparent)
|
|
||||||
require.Nil(t, args.Grandparent.Parent)
|
|
||||||
assert.Equal(t, args.Grandparent, p.Subcommand())
|
|
||||||
assert.Equal(t, []string{"grandparent"}, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args root
|
|
||||||
p, err := pparse("", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Nil(t, args.Grandparent)
|
|
||||||
assert.Nil(t, p.Subcommand())
|
|
||||||
assert.Empty(t, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNestedSubcommandsWithAliases(t *testing.T) {
|
|
||||||
type child struct{}
|
|
||||||
type parent struct {
|
|
||||||
Child *child `arg:"subcommand:child|ch"`
|
|
||||||
}
|
|
||||||
type grandparent struct {
|
|
||||||
Parent *parent `arg:"subcommand:parent|pa"`
|
|
||||||
}
|
|
||||||
type root struct {
|
|
||||||
Grandparent *grandparent `arg:"subcommand:grandparent|gp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args root
|
|
||||||
p, err := pparse("gp parent child", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, args.Grandparent)
|
|
||||||
require.NotNil(t, args.Grandparent.Parent)
|
|
||||||
require.NotNil(t, args.Grandparent.Parent.Child)
|
|
||||||
assert.Equal(t, args.Grandparent.Parent.Child, p.Subcommand())
|
|
||||||
assert.Equal(t, []string{"gp", "parent", "child"}, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args root
|
|
||||||
p, err := pparse("grandparent pa", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, args.Grandparent)
|
|
||||||
require.NotNil(t, args.Grandparent.Parent)
|
|
||||||
require.Nil(t, args.Grandparent.Parent.Child)
|
|
||||||
assert.Equal(t, args.Grandparent.Parent, p.Subcommand())
|
|
||||||
assert.Equal(t, []string{"grandparent", "pa"}, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args root
|
|
||||||
p, err := pparse("grandparent", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, args.Grandparent)
|
|
||||||
require.Nil(t, args.Grandparent.Parent)
|
|
||||||
assert.Equal(t, args.Grandparent, p.Subcommand())
|
|
||||||
assert.Equal(t, []string{"grandparent"}, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args root
|
|
||||||
p, err := pparse("", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Nil(t, args.Grandparent)
|
|
||||||
assert.Nil(t, p.Subcommand())
|
|
||||||
assert.Empty(t, p.SubcommandNames())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubcommandsWithPositionals(t *testing.T) {
|
|
||||||
type listCmd struct {
|
|
||||||
Pattern string `arg:"positional"`
|
|
||||||
}
|
|
||||||
type cmd struct {
|
|
||||||
Format string
|
|
||||||
List *listCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("list", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, "", args.List.Pattern)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("list --format json", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, "", args.List.Pattern)
|
|
||||||
assert.Equal(t, "json", args.Format)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("list somepattern", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, "somepattern", args.List.Pattern)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("list somepattern --format json", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, "somepattern", args.List.Pattern)
|
|
||||||
assert.Equal(t, "json", args.Format)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("list --format json somepattern", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, "somepattern", args.List.Pattern)
|
|
||||||
assert.Equal(t, "json", args.Format)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("--format json list somepattern", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.List)
|
|
||||||
assert.Equal(t, "somepattern", args.List.Pattern)
|
|
||||||
assert.Equal(t, "json", args.Format)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("--format json", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Nil(t, args.List)
|
|
||||||
assert.Equal(t, "json", args.Format)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestSubcommandsWithMultiplePositionals(t *testing.T) {
|
|
||||||
type getCmd struct {
|
|
||||||
Items []string `arg:"positional"`
|
|
||||||
}
|
|
||||||
type cmd struct {
|
|
||||||
Limit int
|
|
||||||
Get *getCmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("get", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.Get)
|
|
||||||
assert.Empty(t, args.Get.Items)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("get --limit 5", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.Get)
|
|
||||||
assert.Empty(t, args.Get.Items)
|
|
||||||
assert.Equal(t, 5, args.Limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("get item1", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.Get)
|
|
||||||
assert.Equal(t, []string{"item1"}, args.Get.Items)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("get item1 item2 item3", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.Get)
|
|
||||||
assert.Equal(t, []string{"item1", "item2", "item3"}, args.Get.Items)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var args cmd
|
|
||||||
err := parse("get item1 --limit 5 item2", &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.NotNil(t, args.Get)
|
|
||||||
assert.Equal(t, []string{"item1", "item2"}, args.Get.Items)
|
|
||||||
assert.Equal(t, 5, args.Limit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValForNilStruct(t *testing.T) {
|
|
||||||
type subcmd struct{}
|
|
||||||
var cmd struct {
|
|
||||||
Sub *subcmd `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := NewParser(Config{}, &cmd)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
typ := reflect.TypeOf(cmd)
|
|
||||||
subField, _ := typ.FieldByName("Sub")
|
|
||||||
|
|
||||||
v := p.val(path{fields: []reflect.StructField{subField, subField}})
|
|
||||||
assert.False(t, v.IsValid())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubcommandInvalidInternal(t *testing.T) {
|
|
||||||
// this situation should never arise in practice but still good to test for it
|
|
||||||
var cmd struct{}
|
|
||||||
p, err := NewParser(Config{}, &cmd)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
p.subcommand = []string{"should", "never", "happen"}
|
|
||||||
sub := p.Subcommand()
|
|
||||||
assert.Nil(t, sub)
|
|
||||||
}
|
|
717
usage_test.go
717
usage_test.go
|
@ -1,717 +0,0 @@
|
||||||
package arg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
type NameDotName struct {
|
|
||||||
Head, Tail string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NameDotName) UnmarshalText(b []byte) error {
|
|
||||||
s := string(b)
|
|
||||||
pos := strings.Index(s, ".")
|
|
||||||
if pos == -1 {
|
|
||||||
return fmt.Errorf("missing period in %s", s)
|
|
||||||
}
|
|
||||||
n.Head = s[:pos]
|
|
||||||
n.Tail = s[pos+1:]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NameDotName) MarshalText() (text []byte, err error) {
|
|
||||||
text = []byte(fmt.Sprintf("%s.%s", n.Head, n.Tail))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWriteUsage(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--testenv TESTENV] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--testenv TESTENV] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]
|
|
||||||
|
|
||||||
Positional arguments:
|
|
||||||
INPUT
|
|
||||||
OUTPUT list of outputs
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--name NAME name to use [default: Foo Bar]
|
|
||||||
--value VALUE secret value [default: 42]
|
|
||||||
--verbose, -v verbosity level
|
|
||||||
--dataset DATASET dataset to use
|
|
||||||
--optimize OPTIMIZE, -O OPTIMIZE
|
|
||||||
optimization level
|
|
||||||
--ids IDS Ids
|
|
||||||
--values VALUES Values
|
|
||||||
--workers WORKERS, -w WORKERS
|
|
||||||
number of workers to start [default: 10, env: WORKERS]
|
|
||||||
--testenv TESTENV, -a TESTENV [env: TEST_ENV]
|
|
||||||
--file FILE, -f FILE File with mandatory extension [default: scratch.txt]
|
|
||||||
--help, -h display this help and exit
|
|
||||||
|
|
||||||
Environment variables:
|
|
||||||
API_KEY Required. Only via env-var for security reasons
|
|
||||||
TRACE Optional. Record low-level trace
|
|
||||||
`
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Input string `arg:"positional,required"`
|
|
||||||
Output []string `arg:"positional" help:"list of outputs"`
|
|
||||||
Name string `help:"name to use"`
|
|
||||||
Value int `help:"secret value"`
|
|
||||||
Verbose bool `arg:"-v" help:"verbosity level"`
|
|
||||||
Dataset string `help:"dataset to use"`
|
|
||||||
Optimize int `arg:"-O" help:"optimization level"`
|
|
||||||
Ids []int64 `help:"Ids"`
|
|
||||||
Values []float64 `help:"Values"`
|
|
||||||
Workers int `arg:"-w,env:WORKERS" help:"number of workers to start" default:"10"`
|
|
||||||
TestEnv string `arg:"-a,env:TEST_ENV"`
|
|
||||||
ApiKey string `arg:"required,-,--,env:API_KEY" help:"Only via env-var for security reasons"`
|
|
||||||
Trace bool `arg:"-,--,env" help:"Record low-level trace"`
|
|
||||||
File *NameDotName `arg:"-f" help:"File with mandatory extension"`
|
|
||||||
}
|
|
||||||
args.Name = "Foo Bar"
|
|
||||||
args.Value = 42
|
|
||||||
args.File = &NameDotName{"scratch", "txt"}
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
os.Args[0] = "example"
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
type MyEnum int
|
|
||||||
|
|
||||||
func (n *MyEnum) UnmarshalText(b []byte) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *MyEnum) MarshalText() ([]byte, error) {
|
|
||||||
return nil, errors.New("There was a problem")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageWithDefaults(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example [--label LABEL] [--content CONTENT]"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example [--label LABEL] [--content CONTENT]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--label LABEL [default: cat]
|
|
||||||
--content CONTENT [default: dog]
|
|
||||||
--help, -h display this help and exit
|
|
||||||
`
|
|
||||||
var args struct {
|
|
||||||
Label string
|
|
||||||
Content string `default:"dog"`
|
|
||||||
}
|
|
||||||
args.Label = "cat"
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
args.Label = "should_ignore_this"
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageCannotMarshalToString(t *testing.T) {
|
|
||||||
var args struct {
|
|
||||||
Name *MyEnum
|
|
||||||
}
|
|
||||||
v := MyEnum(42)
|
|
||||||
args.Name = &v
|
|
||||||
_, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
assert.EqualError(t, err, `args.Name: error marshaling default value to string: There was a problem`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageLongPositionalWithHelp_legacyForm(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example [VERYLONGPOSITIONALWITHHELP]"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example [VERYLONGPOSITIONALWITHHELP]
|
|
||||||
|
|
||||||
Positional arguments:
|
|
||||||
VERYLONGPOSITIONALWITHHELP
|
|
||||||
this positional argument is very long but cannot include commas
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--help, -h display this help and exit
|
|
||||||
`
|
|
||||||
var args struct {
|
|
||||||
VeryLongPositionalWithHelp string `arg:"positional,help:this positional argument is very long but cannot include commas"`
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageLongPositionalWithHelp_newForm(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example [VERYLONGPOSITIONALWITHHELP]"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example [VERYLONGPOSITIONALWITHHELP]
|
|
||||||
|
|
||||||
Positional arguments:
|
|
||||||
VERYLONGPOSITIONALWITHHELP
|
|
||||||
this positional argument is very long, and includes: commas, colons etc
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--help, -h display this help and exit
|
|
||||||
`
|
|
||||||
var args struct {
|
|
||||||
VeryLongPositionalWithHelp string `arg:"positional" help:"this positional argument is very long, and includes: commas, colons etc"`
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageWithProgramName(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: myprogram"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: myprogram
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--help, -h display this help and exit
|
|
||||||
`
|
|
||||||
config := Config{
|
|
||||||
Program: "myprogram",
|
|
||||||
}
|
|
||||||
p, err := NewParser(config, &struct{}{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
os.Args[0] = "example"
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
type versioned struct{}
|
|
||||||
|
|
||||||
// Version returns the version for this program
|
|
||||||
func (versioned) Version() string {
|
|
||||||
return "example 3.2.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageWithVersion(t *testing.T) {
|
|
||||||
expectedUsage := "example 3.2.1\nUsage: example"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
example 3.2.1
|
|
||||||
Usage: example
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--help, -h display this help and exit
|
|
||||||
--version display version and exit
|
|
||||||
`
|
|
||||||
os.Args[0] = "example"
|
|
||||||
p, err := NewParser(Config{}, &versioned{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
type described struct{}
|
|
||||||
|
|
||||||
// Described returns the description for this program
|
|
||||||
func (described) Description() string {
|
|
||||||
return "this program does this and that"
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageWithDescription(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
this program does this and that
|
|
||||||
Usage: example
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--help, -h display this help and exit
|
|
||||||
`
|
|
||||||
os.Args[0] = "example"
|
|
||||||
p, err := NewParser(Config{}, &described{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
type epilogued struct{}
|
|
||||||
|
|
||||||
// Epilogued returns the epilogue for this program
|
|
||||||
func (epilogued) Epilogue() string {
|
|
||||||
return "For more information visit github.com/alexflint/go-arg"
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageWithEpilogue(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--help, -h display this help and exit
|
|
||||||
|
|
||||||
For more information visit github.com/alexflint/go-arg
|
|
||||||
`
|
|
||||||
os.Args[0] = "example"
|
|
||||||
p, err := NewParser(Config{}, &epilogued{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageForRequiredPositionals(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example REQUIRED1 REQUIRED2\n"
|
|
||||||
var args struct {
|
|
||||||
Required1 string `arg:"positional,required"`
|
|
||||||
Required2 string `arg:"positional,required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, usage.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageForMixedPositionals(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example REQUIRED1 REQUIRED2 [OPTIONAL1 [OPTIONAL2]]\n"
|
|
||||||
var args struct {
|
|
||||||
Required1 string `arg:"positional,required"`
|
|
||||||
Required2 string `arg:"positional,required"`
|
|
||||||
Optional1 string `arg:"positional"`
|
|
||||||
Optional2 string `arg:"positional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, usage.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageForRepeatedPositionals(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example REQUIRED1 REQUIRED2 REPEATED [REPEATED ...]\n"
|
|
||||||
var args struct {
|
|
||||||
Required1 string `arg:"positional,required"`
|
|
||||||
Required2 string `arg:"positional,required"`
|
|
||||||
Repeated []string `arg:"positional,required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, usage.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageForMixedAndRepeatedPositionals(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example REQUIRED1 REQUIRED2 [OPTIONAL1 [OPTIONAL2 [REPEATED [REPEATED ...]]]]\n"
|
|
||||||
var args struct {
|
|
||||||
Required1 string `arg:"positional,required"`
|
|
||||||
Required2 string `arg:"positional,required"`
|
|
||||||
Optional1 string `arg:"positional"`
|
|
||||||
Optional2 string `arg:"positional"`
|
|
||||||
Repeated []string `arg:"positional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, usage.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRequiredMultiplePositionals(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example REQUIREDMULTIPLE [REQUIREDMULTIPLE ...]\n"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example REQUIREDMULTIPLE [REQUIREDMULTIPLE ...]
|
|
||||||
|
|
||||||
Positional arguments:
|
|
||||||
REQUIREDMULTIPLE required multiple positional
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--help, -h display this help and exit
|
|
||||||
`
|
|
||||||
var args struct {
|
|
||||||
RequiredMultiple []string `arg:"positional,required" help:"required multiple positional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, usage.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageWithNestedSubcommands(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example child nested [--enable] OUTPUT"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example child nested [--enable] OUTPUT
|
|
||||||
|
|
||||||
Positional arguments:
|
|
||||||
OUTPUT
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--enable
|
|
||||||
|
|
||||||
Global options:
|
|
||||||
--values VALUES Values
|
|
||||||
--verbose, -v verbosity level
|
|
||||||
--help, -h display this help and exit
|
|
||||||
`
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Verbose bool `arg:"-v" help:"verbosity level"`
|
|
||||||
Child *struct {
|
|
||||||
Values []float64 `help:"Values"`
|
|
||||||
Nested *struct {
|
|
||||||
Enable bool
|
|
||||||
Output string `arg:"positional,required"`
|
|
||||||
} `arg:"subcommand:nested"`
|
|
||||||
} `arg:"subcommand:child"`
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Args[0] = "example"
|
|
||||||
p, err := NewParser(Config{}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_ = p.Parse([]string{"child", "nested", "value"})
|
|
||||||
|
|
||||||
assert.Equal(t, []string{"child", "nested"}, p.SubcommandNames())
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var help2 bytes.Buffer
|
|
||||||
p.WriteHelpForSubcommand(&help2, "child", "nested")
|
|
||||||
assert.Equal(t, expectedHelp[1:], help2.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
|
|
||||||
var usage2 bytes.Buffer
|
|
||||||
p.WriteUsageForSubcommand(&usage2, "child", "nested")
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage2.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNonexistentSubcommand(t *testing.T) {
|
|
||||||
var args struct {
|
|
||||||
sub *struct{} `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
p, err := NewParser(Config{Exit: func(int) {}}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
|
|
||||||
err = p.WriteUsageForSubcommand(&b, "does_not_exist")
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
err = p.WriteHelpForSubcommand(&b, "does_not_exist")
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
err = p.FailSubcommand("something went wrong", "does_not_exist")
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
err = p.WriteUsageForSubcommand(&b, "sub", "does_not_exist")
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
err = p.WriteHelpForSubcommand(&b, "sub", "does_not_exist")
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
err = p.FailSubcommand("something went wrong", "sub", "does_not_exist")
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageWithoutLongNames(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example [-a PLACEHOLDER] -b SHORTONLY2"
|
|
||||||
|
|
||||||
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)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageWithShortFirst(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example [-c CAT] [--dog DOG]"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example [-c CAT] [--dog DOG]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-c CAT
|
|
||||||
--dog DOG
|
|
||||||
--help, -h display this help and exit
|
|
||||||
`
|
|
||||||
var args struct {
|
|
||||||
Dog string
|
|
||||||
Cat string `arg:"-c,--"`
|
|
||||||
}
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageWithEnvOptions(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example [-s SHORT]"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example [-s SHORT]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-s SHORT [env: SHORT]
|
|
||||||
--help, -h display this help and exit
|
|
||||||
|
|
||||||
Environment variables:
|
|
||||||
ENVONLY Optional.
|
|
||||||
ENVONLY2 Optional.
|
|
||||||
CUSTOM Optional.
|
|
||||||
`
|
|
||||||
var args struct {
|
|
||||||
Short string `arg:"--,-s,env"`
|
|
||||||
EnvOnly string `arg:"--,env"`
|
|
||||||
EnvOnly2 string `arg:"--,-,env"`
|
|
||||||
EnvOnlyOverriden string `arg:"--,env:CUSTOM"`
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEnvOnlyArgs(t *testing.T) {
|
|
||||||
expectedUsage := "Usage: example [--arg ARG]"
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example [--arg ARG]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--arg ARG, -a ARG [env: MY_ARG]
|
|
||||||
--help, -h display this help and exit
|
|
||||||
|
|
||||||
Environment variables:
|
|
||||||
AUTH_KEY Required.
|
|
||||||
`
|
|
||||||
var args struct {
|
|
||||||
ArgParam string `arg:"-a,--arg,env:MY_ARG"`
|
|
||||||
AuthKey string `arg:"required,--,env:AUTH_KEY"`
|
|
||||||
}
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFail(t *testing.T) {
|
|
||||||
var stdout bytes.Buffer
|
|
||||||
var exitCode int
|
|
||||||
exit := func(code int) { exitCode = code }
|
|
||||||
|
|
||||||
expectedStdout := `
|
|
||||||
Usage: example [--foo FOO]
|
|
||||||
error: something went wrong
|
|
||||||
`
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Foo int
|
|
||||||
}
|
|
||||||
p, err := NewParser(Config{Program: "example", Exit: exit, Out: &stdout}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
p.Fail("something went wrong")
|
|
||||||
|
|
||||||
assert.Equal(t, expectedStdout[1:], stdout.String())
|
|
||||||
assert.Equal(t, -1, exitCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFailSubcommand(t *testing.T) {
|
|
||||||
var stdout bytes.Buffer
|
|
||||||
var exitCode int
|
|
||||||
exit := func(code int) { exitCode = code }
|
|
||||||
|
|
||||||
expectedStdout := `
|
|
||||||
Usage: example sub
|
|
||||||
error: something went wrong
|
|
||||||
`
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Sub *struct{} `arg:"subcommand"`
|
|
||||||
}
|
|
||||||
p, err := NewParser(Config{Program: "example", Exit: exit, Out: &stdout}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = p.FailSubcommand("something went wrong", "sub")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, expectedStdout[1:], stdout.String())
|
|
||||||
assert.Equal(t, -1, exitCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
type lengthOf struct {
|
|
||||||
Length int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *lengthOf) UnmarshalText(b []byte) error {
|
|
||||||
p.Length = len(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHelpShowsDefaultValueFromOriginalTag(t *testing.T) {
|
|
||||||
// check that the usage text prints the original string from the default tag, not
|
|
||||||
// the serialization of the parsed value
|
|
||||||
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example [--test TEST]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--test TEST [default: some_default_value]
|
|
||||||
--help, -h display this help and exit
|
|
||||||
`
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Test *lengthOf `default:"some_default_value"`
|
|
||||||
}
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHelpShowsSubcommandAliases(t *testing.T) {
|
|
||||||
expectedHelp := `
|
|
||||||
Usage: example <command> [<args>]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--help, -h display this help and exit
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
remove, rm, r remove something from somewhere
|
|
||||||
simple do something simple
|
|
||||||
halt, stop stop now
|
|
||||||
`
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Remove *struct{} `arg:"subcommand:remove|rm|r" help:"remove something from somewhere"`
|
|
||||||
Simple *struct{} `arg:"subcommand" help:"do something simple"`
|
|
||||||
Stop *struct{} `arg:"subcommand:halt|stop" help:"stop now"`
|
|
||||||
}
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
|
||||||
p.WriteHelp(&help)
|
|
||||||
assert.Equal(t, expectedHelp[1:], help.String())
|
|
||||||
}
|
|
Loading…
Reference in New Issue