Browse Source

Merge commit '0c65f0a57ae2898e146746476d34577041e79c52' as 'vendor/github.com/alexflint/go-arg'

master
Brett Langdon 10 years ago
parent
commit
1e65e70289
9 changed files with 1165 additions and 0 deletions
  1. +24
    -0
      vendor/github.com/alexflint/go-arg/.gitignore
  2. +9
    -0
      vendor/github.com/alexflint/go-arg/.travis.yml
  3. +24
    -0
      vendor/github.com/alexflint/go-arg/LICENSE
  4. +129
    -0
      vendor/github.com/alexflint/go-arg/README.md
  5. +87
    -0
      vendor/github.com/alexflint/go-arg/example_test.go
  6. +381
    -0
      vendor/github.com/alexflint/go-arg/parse.go
  7. +358
    -0
      vendor/github.com/alexflint/go-arg/parse_test.go
  8. +107
    -0
      vendor/github.com/alexflint/go-arg/usage.go
  9. +46
    -0
      vendor/github.com/alexflint/go-arg/usage_test.go

+ 24
- 0
vendor/github.com/alexflint/go-arg/.gitignore View File

@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

+ 9
- 0
vendor/github.com/alexflint/go-arg/.travis.yml View File

@ -0,0 +1,9 @@
language: go
go:
- tip
before_install:
- go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls
- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
script:
- $HOME/gopath/bin/goveralls -service=travis-ci

+ 24
- 0
vendor/github.com/alexflint/go-arg/LICENSE View File

@ -0,0 +1,24 @@
Copyright (c) 2015, Alex Flint
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 129
- 0
vendor/github.com/alexflint/go-arg/README.md View File

@ -0,0 +1,129 @@
[![GoDoc](https://godoc.org/github.com/alexflint/go-arg?status.svg)](https://godoc.org/github.com/alexflint/go-arg)
[![Build Status](https://travis-ci.org/alexflint/go-arg.svg?branch=master)](https://travis-ci.org/alexflint/go-arg)
[![Coverage Status](https://coveralls.io/repos/alexflint/go-arg/badge.svg?branch=master&service=github)](https://coveralls.io/github/alexflint/go-arg?branch=master)
## Structured argument parsing for Go
Declare the command line arguments your program accepts by defining a struct.
```go
var args struct {
Foo string
Bar bool
}
arg.MustParse(&args)
fmt.Println(args.Foo, args.Bar)
```
```shell
$ ./example --foo=hello --bar
hello true
```
### Required arguments
```go
var args struct {
Foo string `arg:"required"`
Bar bool
}
arg.MustParse(&args)
```
```shell
$ ./example
usage: example --foo FOO [--bar]
error: --foo is required
```
### Positional arguments
```go
var args struct {
Input string `arg:"positional"`
Output []string `arg:"positional"`
}
arg.MustParse(&args)
fmt.Println("Input:", args.Input)
fmt.Println("Output:", args.Output)
```
```
$ ./example src.txt x.out y.out z.out
Input: src.txt
Output: [x.out y.out z.out]
```
### Usage strings
```go
var args struct {
Input string `arg:"positional"`
Output []string `arg:"positional"`
Verbose bool `arg:"-v,help:verbosity level"`
Dataset string `arg:"help:dataset to use"`
Optimize int `arg:"-O,help:optimization level"`
}
arg.MustParse(&args)
```
```shell
$ ./example -h
usage: [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--help] INPUT [OUTPUT [OUTPUT ...]]
positional arguments:
input
output
options:
--verbose, -v verbosity level
--dataset DATASET dataset to use
--optimize OPTIMIZE, -O OPTIMIZE
optimization level
--help, -h print this help message
```
### Default values
```go
var args struct {
Foo string
Bar bool
}
args.Foo = "default value"
arg.MustParse(&args)
```
### Arguments with multiple values
```go
var args struct {
Database string
IDs []int64
}
arg.MustParse(&args)
fmt.Printf("Fetching the following IDs from %s: %q", args.Database, args.IDs)
```
```shell
./example -database foo -ids 1 2 3
Fetching the following IDs from foo: [1 2 3]
```
### Installation
```shell
go get github.com/alexflint/go-arg
```
### Documentation
https://godoc.org/github.com/alexflint/go-arg
### Rationale
There are many command line argument parsing libraries for Go, including one in the standard library, so why build another?
The shortcomings of the `flag` library that ships in the standard library are well known. Positional arguments must preceed options, so `./prog x --foo=1` does what you expect but `./prog --foo=1 x` does not. Arguments cannot have both long (`--foo`) and short (`-f`) forms.
Many third-party argument parsing libraries are geared for writing sophisticated command line interfaces. The excellent `codegangsta/cli` is perfect for working with multiple sub-commands and nested flags, but is probably overkill for a simple script with a handful of flags.
The main idea behind `go-arg` is that Go already has an excellent way to describe data structures using Go structs, so there is no need to develop more levels of abstraction on top of this. Instead of one API to specify which arguments your program accepts, and then another API to get the values of those arguments, why not replace both with a single struct?

+ 87
- 0
vendor/github.com/alexflint/go-arg/example_test.go View File

@ -0,0 +1,87 @@
package arg
import (
"fmt"
"os"
)
// This example demonstrates basic usage
func Example_Basic() {
// These are the args you would pass in on the command line
os.Args = []string{"./example", "--foo=hello", "--bar"}
var args struct {
Foo string
Bar bool
}
MustParse(&args)
fmt.Println(args.Foo, args.Bar)
}
// 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 = []string{"--help"}
var args struct {
Foo string
Bar bool
}
args.Foo = "default value"
MustParse(&args)
fmt.Println(args.Foo, args.Bar)
}
// This example demonstrates arguments that are required
func Example_RequiredArguments() {
// These are the args you would pass in on the command line
os.Args = []string{"--foo=1", "--bar"}
var args struct {
Foo string `arg:"required"`
Bar bool
}
MustParse(&args)
}
// This example demonstrates positional arguments
func Example_PositionalArguments() {
// These are the args you would pass in on the command line
os.Args = []string{"./example", "in", "out1", "out2", "out3"}
var args struct {
Input string `arg:"positional"`
Output []string `arg:"positional"`
}
MustParse(&args)
fmt.Println("Input:", args.Input)
fmt.Println("Output:", args.Output)
}
// This example demonstrates arguments that have multiple values
func Example_MultipleValues() {
// The args you would pass in on the command line
os.Args = []string{"--help"}
var args struct {
Database string
IDs []int64
}
MustParse(&args)
fmt.Printf("Fetching the following IDs from %s: %q", args.Database, args.IDs)
}
// This example shows the usage string generated by go-arg
func Example_UsageString() {
// These are the args you would pass in on the command line
os.Args = []string{"--help"}
var args struct {
Input string `arg:"positional"`
Output []string `arg:"positional"`
Verbose bool `arg:"-v,help:verbosity level"`
Dataset string `arg:"help:dataset to use"`
Optimize int `arg:"-O,help:optimization level"`
}
MustParse(&args)
}

+ 381
- 0
vendor/github.com/alexflint/go-arg/parse.go View File

@ -0,0 +1,381 @@
// Package arg parses command line arguments using the fields from a struct.
//
// For example,
//
// var args struct {
// Iter int
// Debug bool
// }
// arg.MustParse(&args)
//
// defines two command line arguments, which can be set using any of
//
// ./example --iter=1 --debug // debug is a boolean flag so its value is set to true
// ./example -iter 1 // debug defaults to its zero value (false)
// ./example --debug=true // iter defaults to its zero value (zero)
//
// The fastest way to see how to use go-arg is to read the examples below.
//
// Fields can be bool, string, any float type, or any signed or unsigned integer type.
// They can also be slices of any of the above, or slices of pointers to any of the above.
//
// Tags can be specified using the `arg` package name:
//
// var args struct {
// Input string `arg:"positional"`
// Log string `arg:"positional,required"`
// Debug bool `arg:"-d,help:turn on debug mode"`
// RealMode bool `arg:"--real"
// Wr io.Writer `arg:"-"`
// }
//
// The valid tag strings are `positional`, `required`, and `help`. Further, any tag string
// that starts with a single hyphen is the short form for an argument (e.g. `./example -d`),
// and any tag string that starts with two hyphens is the long form for the argument
// (instead of the field name). Fields can be excluded from processing with `arg:"-"`.
package arg
import (
"errors"
"fmt"
"os"
"reflect"
"strconv"
"strings"
)
// spec represents a command line option
type spec struct {
dest reflect.Value
long string
short string
multiple bool
required bool
positional bool
help string
wasPresent bool
}
// ErrHelp indicates that -h or --help were provided
var ErrHelp = errors.New("help requested by user")
// MustParse processes command line arguments and exits upon failure
func MustParse(dest ...interface{}) {
p, err := NewParser(dest...)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
err = p.Parse(os.Args[1:])
if err == ErrHelp {
p.WriteHelp(os.Stdout)
os.Exit(0)
}
if err != nil {
p.Fail(err.Error())
}
}
// Parse processes command line arguments and stores them in dest
func Parse(dest ...interface{}) error {
p, err := NewParser(dest...)
if err != nil {
return err
}
return p.Parse(os.Args[1:])
}
// Parser represents a set of command line options with destination values
type Parser struct {
spec []*spec
}
// NewParser constructs a parser from a list of destination structs
func NewParser(dests ...interface{}) (*Parser, error) {
var specs []*spec
for _, dest := range dests {
v := reflect.ValueOf(dest)
if v.Kind() != reflect.Ptr {
panic(fmt.Sprintf("%s is not a pointer (did you forget an ampersand?)", v.Type()))
}
v = v.Elem()
if v.Kind() != reflect.Struct {
panic(fmt.Sprintf("%T is not a struct pointer", dest))
}
t := v.Type()
for i := 0; i < t.NumField(); i++ {
// Check for the ignore switch in the tag
field := t.Field(i)
tag := field.Tag.Get("arg")
if tag == "-" {
continue
}
spec := spec{
long: strings.ToLower(field.Name),
dest: v.Field(i),
}
// Get the scalar type for this field
scalarType := field.Type
if scalarType.Kind() == reflect.Slice {
spec.multiple = true
scalarType = scalarType.Elem()
if scalarType.Kind() == reflect.Ptr {
scalarType = scalarType.Elem()
}
}
// Check for unsupported types
switch scalarType.Kind() {
case reflect.Array, reflect.Chan, reflect.Func, reflect.Interface,
reflect.Map, reflect.Ptr, reflect.Struct,
reflect.Complex64, reflect.Complex128:
return nil, fmt.Errorf("%s.%s: %s fields are not supported", t.Name(), field.Name, scalarType.Kind())
}
// Look at the tag
if tag != "" {
for _, key := range strings.Split(tag, ",") {
var value string
if pos := strings.Index(key, ":"); pos != -1 {
value = key[pos+1:]
key = key[:pos]
}
switch {
case strings.HasPrefix(key, "--"):
spec.long = key[2:]
case strings.HasPrefix(key, "-"):
if len(key) != 2 {
return nil, fmt.Errorf("%s.%s: short arguments must be one character only", t.Name(), field.Name)
}
spec.short = key[1:]
case key == "required":
spec.required = true
case key == "positional":
spec.positional = true
case key == "help":
spec.help = value
default:
return nil, fmt.Errorf("unrecognized tag '%s' on field %s", key, tag)
}
}
}
specs = append(specs, &spec)
}
}
return &Parser{spec: specs}, nil
}
// Parse processes the given command line option, storing the results in the field
// of the structs from which NewParser was constructed
func (p *Parser) Parse(args []string) error {
// If -h or --help were specified then print usage
for _, arg := range args {
if arg == "-h" || arg == "--help" {
return ErrHelp
}
if arg == "--" {
break
}
}
// Process all command line arguments
err := process(p.spec, args)
if err != nil {
return err
}
// Validate
return validate(p.spec)
}
// process goes through arguments one-by-one, parses them, and assigns the result to
// the underlying struct field
func process(specs []*spec, args []string) error {
// construct a map from --option to spec
optionMap := make(map[string]*spec)
for _, spec := range specs {
if spec.positional {
continue
}
if spec.long != "" {
optionMap[spec.long] = spec
}
if spec.short != "" {
optionMap[spec.short] = spec
}
}
// process each string from the command line
var allpositional bool
var positionals []string
// must use explicit for loop, not range, because we manipulate i inside the loop
for i := 0; i < len(args); i++ {
arg := args[i]
if arg == "--" {
allpositional = true
continue
}
if !strings.HasPrefix(arg, "-") || allpositional {
positionals = append(positionals, arg)
continue
}
// check for an equals sign, as in "--foo=bar"
var value string
opt := strings.TrimLeft(arg, "-")
if pos := strings.Index(opt, "="); pos != -1 {
value = opt[pos+1:]
opt = opt[:pos]
}
// lookup the spec for this option
spec, ok := optionMap[opt]
if !ok {
return fmt.Errorf("unknown argument %s", arg)
}
spec.wasPresent = true
// deal with the case of multiple values
if spec.multiple {
var values []string
if value == "" {
for i+1 < len(args) && !strings.HasPrefix(args[i+1], "-") {
values = append(values, args[i+1])
i++
}
} else {
values = append(values, value)
}
err := setSlice(spec.dest, values)
if err != nil {
return fmt.Errorf("error processing %s: %v", arg, err)
}
continue
}
// if it's a flag and it has no value then set the value to true
if spec.dest.Kind() == reflect.Bool && value == "" {
value = "true"
}
// if we have something like "--foo" then the value is the next argument
if value == "" {
if i+1 == len(args) || strings.HasPrefix(args[i+1], "-") {
return fmt.Errorf("missing value for %s", arg)
}
value = args[i+1]
i++
}
err := setScalar(spec.dest, value)
if err != nil {
return fmt.Errorf("error processing %s: %v", arg, err)
}
}
// process positionals
for _, spec := range specs {
if spec.positional {
if spec.multiple {
err := setSlice(spec.dest, positionals)
if err != nil {
return fmt.Errorf("error processing %s: %v", spec.long, err)
}
positionals = nil
} else if len(positionals) > 0 {
err := setScalar(spec.dest, positionals[0])
if err != nil {
return fmt.Errorf("error processing %s: %v", spec.long, err)
}
positionals = positionals[1:]
} else if spec.required {
return fmt.Errorf("%s is required", spec.long)
}
}
}
if len(positionals) > 0 {
return fmt.Errorf("too many positional arguments at '%s'", positionals[0])
}
return nil
}
// validate an argument spec after arguments have been parse
func validate(spec []*spec) error {
for _, arg := range spec {
if !arg.positional && arg.required && !arg.wasPresent {
return fmt.Errorf("--%s is required", arg.long)
}
}
return nil
}
// parse a value as the apropriate type and store it in the struct
func setSlice(dest reflect.Value, values []string) error {
if !dest.CanSet() {
return fmt.Errorf("field is not writable")
}
var ptr bool
elem := dest.Type().Elem()
if elem.Kind() == reflect.Ptr {
ptr = true
elem = elem.Elem()
}
for _, s := range values {
v := reflect.New(elem)
if err := setScalar(v.Elem(), s); err != nil {
return err
}
if !ptr {
v = v.Elem()
}
dest.Set(reflect.Append(dest, v))
}
return nil
}
// set a value from a string
func setScalar(v reflect.Value, s string) error {
if !v.CanSet() {
return fmt.Errorf("field is not exported")
}
switch v.Kind() {
case reflect.String:
v.Set(reflect.ValueOf(s))
case reflect.Bool:
x, err := strconv.ParseBool(s)
if err != nil {
return err
}
v.Set(reflect.ValueOf(x))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
x, err := strconv.ParseInt(s, 10, v.Type().Bits())
if err != nil {
return err
}
v.Set(reflect.ValueOf(x).Convert(v.Type()))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
x, err := strconv.ParseUint(s, 10, v.Type().Bits())
if err != nil {
return err
}
v.Set(reflect.ValueOf(x).Convert(v.Type()))
case reflect.Float32, reflect.Float64:
x, err := strconv.ParseFloat(s, v.Type().Bits())
if err != nil {
return err
}
v.Set(reflect.ValueOf(x).Convert(v.Type()))
default:
return fmt.Errorf("not a scalar type: %s", v.Kind())
}
return nil
}

+ 358
- 0
vendor/github.com/alexflint/go-arg/parse_test.go View File

@ -0,0 +1,358 @@
package arg
import (
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func parse(cmdline string, dest interface{}) error {
p, err := NewParser(dest)
if err != nil {
return err
}
return p.Parse(strings.Split(cmdline, " "))
}
func TestStringSingle(t *testing.T) {
var args struct {
Foo string
}
err := parse("--foo bar", &args)
require.NoError(t, err)
assert.Equal(t, "bar", args.Foo)
}
func TestMixed(t *testing.T) {
var args struct {
Foo string `arg:"-f"`
Bar int
Baz uint `arg:"positional"`
Ham bool
Spam float32
}
args.Bar = 3
err := parse("123 -spam=1.2 -ham -f xyz", &args)
require.NoError(t, err)
assert.Equal(t, "xyz", args.Foo)
assert.Equal(t, 3, args.Bar)
assert.Equal(t, uint(123), args.Baz)
assert.Equal(t, true, args.Ham)
assert.EqualValues(t, 1.2, args.Spam)
}
func TestRequired(t *testing.T) {
var args struct {
Foo string `arg:"required"`
}
err := parse("", &args)
require.Error(t, err, "--foo is required")
}
func TestShortFlag(t *testing.T) {
var args struct {
Foo string `arg:"-f"`
}
err := parse("-f xyz", &args)
require.NoError(t, err)
assert.Equal(t, "xyz", args.Foo)
err = parse("-foo xyz", &args)
require.NoError(t, err)
assert.Equal(t, "xyz", args.Foo)
err = parse("--foo xyz", &args)
require.NoError(t, err)
assert.Equal(t, "xyz", args.Foo)
}
func TestInvalidShortFlag(t *testing.T) {
var args struct {
Foo string `arg:"-foo"`
}
err := parse("", &args)
assert.Error(t, err)
}
func TestLongFlag(t *testing.T) {
var args struct {
Foo string `arg:"--abc"`
}
err := parse("-abc xyz", &args)
require.NoError(t, err)
assert.Equal(t, "xyz", args.Foo)
err = parse("--abc xyz", &args)
require.NoError(t, err)
assert.Equal(t, "xyz", args.Foo)
}
func TestCaseSensitive(t *testing.T) {
var args struct {
Lower bool `arg:"-v"`
Upper bool `arg:"-V"`
}
err := parse("-v", &args)
require.NoError(t, err)
assert.True(t, args.Lower)
assert.False(t, args.Upper)
}
func TestCaseSensitive2(t *testing.T) {
var args struct {
Lower bool `arg:"-v"`
Upper bool `arg:"-V"`
}
err := parse("-V", &args)
require.NoError(t, err)
assert.False(t, args.Lower)
assert.True(t, args.Upper)
}
func TestPositional(t *testing.T) {
var args struct {
Input string `arg:"positional"`
Output string `arg:"positional"`
}
err := parse("foo", &args)
require.NoError(t, err)
assert.Equal(t, "foo", args.Input)
assert.Equal(t, "", args.Output)
}
func TestPositionalPointer(t *testing.T) {
var args struct {
Input string `arg:"positional"`
Output []*string `arg:"positional"`
}
err := parse("foo bar baz", &args)
require.NoError(t, err)
assert.Equal(t, "foo", args.Input)
bar := "bar"
baz := "baz"
assert.Equal(t, []*string{&bar, &baz}, args.Output)
}
func TestRequiredPositional(t *testing.T) {
var args struct {
Input string `arg:"positional"`
Output string `arg:"positional,required"`
}
err := parse("foo", &args)
assert.Error(t, err)
}
func TestTooManyPositional(t *testing.T) {
var args struct {
Input string `arg:"positional"`
Output string `arg:"positional"`
}
err := parse("foo bar baz", &args)
assert.Error(t, err)
}
func TestMultiple(t *testing.T) {
var args struct {
Foo []int
Bar []string
}
err := parse("--foo 1 2 3 --bar x y z", &args)
require.NoError(t, err)
assert.Equal(t, []int{1, 2, 3}, args.Foo)
assert.Equal(t, []string{"x", "y", "z"}, args.Bar)
}
func TestMultipleWithEq(t *testing.T) {
var args struct {
Foo []int
Bar []string
}
err := parse("--foo 1 2 3 --bar=x", &args)
require.NoError(t, err)
assert.Equal(t, []int{1, 2, 3}, args.Foo)
assert.Equal(t, []string{"x"}, args.Bar)
}
func TestExemptField(t *testing.T) {
var args struct {
Foo string
Bar interface{} `arg:"-"`
}
err := parse("--foo xyz", &args)
require.NoError(t, err)
assert.Equal(t, "xyz", args.Foo)
}
func TestUnknownField(t *testing.T) {
var args struct {
Foo string
}
err := parse("--bar xyz", &args)
assert.Error(t, err)
}
func TestMissingRequired(t *testing.T) {
var args struct {
Foo string `arg:"required"`
X []string `arg:"positional"`
}
err := parse("x", &args)
assert.Error(t, err)
}
func TestMissingValue(t *testing.T) {
var args struct {
Foo string
}
err := parse("--foo", &args)
assert.Error(t, err)
}
func TestInvalidInt(t *testing.T) {
var args struct {
Foo int
}
err := parse("--foo=xyz", &args)
assert.Error(t, err)
}
func TestInvalidUint(t *testing.T) {
var args struct {
Foo uint
}
err := parse("--foo=xyz", &args)
assert.Error(t, err)
}
func TestInvalidFloat(t *testing.T) {
var args struct {
Foo float64
}
err := parse("--foo xyz", &args)
require.Error(t, err)
}
func TestInvalidBool(t *testing.T) {
var args struct {
Foo bool
}
err := parse("--foo=xyz", &args)
require.Error(t, err)
}
func TestInvalidIntSlice(t *testing.T) {
var args struct {
Foo []int
}
err := parse("--foo 1 2 xyz", &args)
require.Error(t, err)
}
func TestInvalidPositional(t *testing.T) {
var args struct {
Foo int `arg:"positional"`
}
err := parse("xyz", &args)
require.Error(t, err)
}
func TestInvalidPositionalSlice(t *testing.T) {
var args struct {
Foo []int `arg:"positional"`
}
err := parse("1 2 xyz", &args)
require.Error(t, err)
}
func TestNoMoreOptions(t *testing.T) {
var args struct {
Foo string
Bar []string `arg:"positional"`
}
err := parse("abc -- --foo xyz", &args)
require.NoError(t, err)
assert.Equal(t, "", args.Foo)
assert.Equal(t, []string{"abc", "--foo", "xyz"}, args.Bar)
}
func TestHelpFlag(t *testing.T) {
var args struct {
Foo string
Bar interface{} `arg:"-"`
}
err := parse("--help", &args)
assert.Equal(t, ErrHelp, err)
}
func TestPanicOnNonPointer(t *testing.T) {
var args struct{}
assert.Panics(t, func() {
parse("", args)
})
}
func TestPanicOnNonStruct(t *testing.T) {
var args string
assert.Panics(t, func() {
parse("", &args)
})
}
func TestUnsupportedType(t *testing.T) {
var args struct {
Foo interface{}
}
err := parse("--foo", &args)
assert.Error(t, err)
}
func TestUnsupportedSliceElement(t *testing.T) {
var args struct {
Foo []interface{}
}
err := parse("--foo", &args)
assert.Error(t, err)
}
func TestUnknownTag(t *testing.T) {
var args struct {
Foo string `arg:"this_is_not_valid"`
}
err := parse("--foo xyz", &args)
assert.Error(t, err)
}
func TestParse(t *testing.T) {
var args struct {
Foo string
}
os.Args = []string{"example", "--foo", "bar"}
err := Parse(&args)
require.NoError(t, err)
assert.Equal(t, "bar", args.Foo)
}
func TestParseError(t *testing.T) {
var args struct {
Foo string `arg:"this_is_not_valid"`
}
os.Args = []string{"example", "--bar"}
err := Parse(&args)
assert.Error(t, err)
}
func TestMustParse(t *testing.T) {
var args struct {
Foo string
}
os.Args = []string{"example", "--foo", "bar"}
MustParse(&args)
assert.Equal(t, "bar", args.Foo)
}

+ 107
- 0
vendor/github.com/alexflint/go-arg/usage.go View File

@ -0,0 +1,107 @@
package arg
import (
"fmt"
"io"
"os"
"path/filepath"
"reflect"
"strings"
)
// Fail prints usage information to stdout and exits with non-zero status
func (p *Parser) Fail(msg string) {
p.WriteUsage(os.Stdout)
fmt.Println("error:", msg)
os.Exit(-1)
}
// WriteUsage writes usage information to the given writer
func (p *Parser) WriteUsage(w io.Writer) {
var positionals, options []*spec
for _, spec := range p.spec {
if spec.positional {
positionals = append(positionals, spec)
} else {
options = append(options, spec)
}
}
fmt.Fprintf(w, "usage: %s ", filepath.Base(os.Args[0]))
// write the option component of the usage message
for _, spec := range options {
if !spec.required {
fmt.Fprint(w, "[")
}
fmt.Fprint(w, synopsis(spec, "--"+spec.long))
if !spec.required {
fmt.Fprint(w, "]")
}
fmt.Fprint(w, " ")
}
// write the positional component of the usage message
for _, spec := range positionals {
up := strings.ToUpper(spec.long)
if spec.multiple {
fmt.Fprintf(w, "[%s [%s ...]]", up, up)
} else {
fmt.Fprint(w, up)
}
fmt.Fprint(w, " ")
}
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) {
var positionals, options []*spec
for _, spec := range p.spec {
if spec.positional {
positionals = append(positionals, spec)
} else {
options = append(options, spec)
}
}
p.WriteUsage(w)
// write the list of positionals
if len(positionals) > 0 {
fmt.Fprint(w, "\npositional arguments:\n")
for _, spec := range positionals {
fmt.Fprintf(w, " %s\n", spec.long)
}
}
// write the list of options
if len(options) > 0 {
fmt.Fprint(w, "\noptions:\n")
const colWidth = 25
for _, spec := range options {
left := " " + synopsis(spec, "--"+spec.long)
if spec.short != "" {
left += ", " + synopsis(spec, "-"+spec.short)
}
fmt.Fprint(w, left)
if spec.help != "" {
if len(left)+2 < colWidth {
fmt.Fprint(w, strings.Repeat(" ", colWidth-len(left)))
} else {
fmt.Fprint(w, "\n"+strings.Repeat(" ", colWidth))
}
fmt.Fprint(w, spec.help)
}
fmt.Fprint(w, "\n")
}
}
}
func synopsis(spec *spec, form string) string {
if spec.dest.Kind() == reflect.Bool {
return form
} else {
return form + " " + strings.ToUpper(spec.long)
}
}

+ 46
- 0
vendor/github.com/alexflint/go-arg/usage_test.go View File

@ -0,0 +1,46 @@
package arg
import (
"bytes"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestWriteUsage(t *testing.T) {
expectedUsage := "usage: example [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] INPUT [OUTPUT [OUTPUT ...]] \n"
expectedHelp := `usage: example [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] INPUT [OUTPUT [OUTPUT ...]]
positional arguments:
input
output
options:
--verbose, -v verbosity level
--dataset DATASET dataset to use
--optimize OPTIMIZE, -O OPTIMIZE
optimization level
`
var args struct {
Input string `arg:"positional"`
Output []string `arg:"positional"`
Verbose bool `arg:"-v,help:verbosity level"`
Dataset string `arg:"help:dataset to use"`
Optimize int `arg:"-O,help:optimization level"`
}
p, err := NewParser(&args)
require.NoError(t, err)
os.Args[0] = "example"
var usage bytes.Buffer
p.WriteUsage(&usage)
assert.Equal(t, expectedUsage, usage.String())
var help bytes.Buffer
p.WriteHelp(&help)
assert.Equal(t, expectedHelp, help.String())
}

Loading…
Cancel
Save