Skip to content

Commit

Permalink
Merge pull request #62 from mwlazlo-tls/master
Browse files Browse the repository at this point in the history
Custom parsers implementing encoding.TextMarshaler() can have default…
  • Loading branch information
alexflint authored Apr 16, 2018
2 parents 0cc8e30 + 51337de commit b1eda2c
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 4 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,15 +265,33 @@ func (n *NameDotName) UnmarshalText(b []byte) error {
return nil
}

// optional: implement in case you want to display a default value in the usage string
func (n *NameDotName) MarshalText() (text []byte, err error) {
text = []byte(fmt.Sprintf("%s.%s", n.Head, n.Tail))
return
}

func main() {
var args struct {
Name *NameDotName
}
// set default
args.Name = &NameDotName{"file", "txt"}
arg.MustParse(&args)
fmt.Printf("%#v\n", args.Name)
}
```
```shell
$ ./example --help
Usage: test [--name NAME]

Options:
--name NAME [default: file.txt]
--help, -h display this help and exit

$ ./example
&main.NameDotName{Head:"file", Tail:"txt"}

$ ./example --name=foo.bar
&main.NameDotName{Head:"foo", Tail:"bar"}

Expand Down
11 changes: 10 additions & 1 deletion usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"reflect"
"strings"
"encoding"
)

// the width of the left column
Expand Down Expand Up @@ -134,7 +135,15 @@ func printOption(w io.Writer, spec *spec) {
if v.IsValid() {
z := reflect.Zero(v.Type())
if (v.Type().Comparable() && z.Type().Comparable() && v.Interface() != z.Interface()) || v.Kind() == reflect.Slice && !v.IsNil() {
fmt.Fprintf(w, " [default: %v]", v)
if scalar, ok := v.Interface().(encoding.TextMarshaler); ok {
if value, err := scalar.MarshalText(); err != nil {
fmt.Fprintf(w, " [default: error: %v]", err)
} else {
fmt.Fprintf(w, " [default: %v]", string(value))
}
} else {
fmt.Fprintf(w, " [default: %v]", v)
}
}
}
fmt.Fprint(w, "\n")
Expand Down
68 changes: 65 additions & 3 deletions usage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,35 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"strings"
"fmt"
"errors"
)

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] INPUT [OUTPUT [OUTPUT ...]]\n"
expectedUsage := "Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]\n"

expectedHelp := `Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] INPUT [OUTPUT [OUTPUT ...]]
expectedHelp := `Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]
Positional arguments:
INPUT
Expand All @@ -29,6 +52,7 @@ Options:
--values VALUES Values [default: [3.14 42 256]]
--workers WORKERS, -w WORKERS
number of workers to start
--file FILE, -f FILE File with mandatory extension [default: scratch.txt]
--help, -h display this help and exit
`
var args struct {
Expand All @@ -42,11 +66,13 @@ Options:
Ids []int64 `help:"Ids"`
Values []float64 `help:"Values"`
Workers int `arg:"-w,env:WORKERS" help:"number of workers to start"`
File *NameDotName `arg:"-f" help:"File with mandatory extension"`
}
args.Name = "Foo Bar"
args.Value = 42
args.Values = []float64{3.14, 42, 256}
p, err := NewParser(Config{}, &args)
args.File = &NameDotName{"scratch", "txt"}
p, err := NewParser(Config{"example"}, &args)
require.NoError(t, err)

os.Args[0] = "example"
Expand All @@ -60,6 +86,42 @@ Options:
assert.Equal(t, expectedHelp, help.String())
}

type MyEnum int

func (n *MyEnum) UnmarshalText(b []byte) error {
b = []byte("Hello")
return nil
}

func (n *MyEnum) MarshalText() (text []byte, err error) {
s := "There was a problem"
text = []byte(s)
err = errors.New(s)
return
}

func TestUsageError(t *testing.T) {
expectedHelp := `Usage: example [--name NAME]
Options:
--name NAME [default: error: There was a problem]
--help, -h display this help and exit
`
var args struct {
Name *MyEnum
}
v := MyEnum(42)
args.Name = &v
p, err := NewParser(Config{"example"}, &args)

// NB: some might might expect there to be an error here
require.NoError(t, err)

var help bytes.Buffer
p.WriteHelp(&help)
assert.Equal(t, expectedHelp, help.String())
}

func TestUsageLongPositionalWithHelp_legacyForm(t *testing.T) {
expectedHelp := `Usage: example VERYLONGPOSITIONALWITHHELP
Expand Down

0 comments on commit b1eda2c

Please sign in to comment.