Kong tag PR fixes (#12)

* Removed bubbling errors in favour of panic
* Added a test for Parse with a bad build and an arg.
* Removed is() function in favour of a switch with strings
* Collect key and value in parseCSV, reducing complexity.
* Fix in global to not use parser instance on error.
This commit is contained in:
Gerald Kaszuba
2018-05-22 23:18:08 +10:00
committed by Alec Thomas
parent 31e1ffaa3b
commit ecd369e622
5 changed files with 80 additions and 59 deletions
+1 -5
View File
@@ -61,11 +61,7 @@ func buildNode(v reflect.Value, seenFlags map[string]bool, cmd bool) *Node {
name = strings.ToLower(dashedString(ft.Name))
}
tag, err := parseTag(fv, ft.Tag.Get("kong"))
if err != nil {
fail("%s", err)
}
tag := parseTag(fv, ft.Tag.Get("kong"))
decoder := DecoderForField(tag.Type, ft)
if !cmd {
+6 -2
View File
@@ -1,11 +1,15 @@
package kong
import "os"
import (
"os"
)
// Parse constructs a new parser and parses the default command-line.
func Parse(cli interface{}, options ...Option) string {
parser, err := New(cli, options...)
parser.FatalIfErrorf(err)
if err != nil {
panic(err)
}
cmd, err := parser.Parse(os.Args[1:])
parser.FatalIfErrorf(err)
return cmd
+31
View File
@@ -0,0 +1,31 @@
package kong
import (
"os"
"testing"
"github.com/stretchr/testify/require"
)
func TestParseHandlingBadBuild(t *testing.T) {
var cli struct {
Enabled bool `kong:"unknown"`
}
args := os.Args
defer func() {
os.Args = args
}()
os.Args = []string{os.Args[0], "hi"}
defer func() {
if r := recover(); r != nil {
require.Equal(t, Error{msg: "unknown is an unknown kong key"}, r)
}
}()
Parse(&cli, ExitFunction(func(_ int) { panic("exiting") }))
require.Fail(t, "we were expecting a panic")
}
+2 -5
View File
@@ -51,6 +51,7 @@ func New(ast interface{}, options ...Option) (*Kong, error) {
for _, option := range options {
option(k)
}
return k, nil
}
@@ -75,11 +76,7 @@ func (k *Kong) Parse(args []string) (command string, err error) {
}
func (k *Kong) Errorf(format string, args ...interface{}) {
if k.Model != nil {
fmt.Fprintf(os.Stderr, k.Model.Name+": "+format, args...)
} else {
fmt.Fprintf(os.Stderr, format, args...)
}
fmt.Fprintf(os.Stderr, k.Model.Name+": "+format, args...)
}
func (k *Kong) FatalIfErrorf(err error, args ...interface{}) {
+40 -47
View File
@@ -1,7 +1,6 @@
package kong
import (
"fmt"
"reflect"
"strings"
"unicode/utf8"
@@ -21,19 +20,21 @@ type Tag struct {
Short rune
}
func parseCSV(s string) ([]string, error) {
num := 0
parts := []string{}
current := []rune{}
func parseCSV(s string) map[string]string {
d := map[string]string{}
key := []rune{}
value := []rune{}
quotes := false
inKey := true
add := func() {
parts = append(parts, string(current))
current = []rune{}
num++
d[string(key)] = string(value)
key = []rune{}
value = []rune{}
inKey = true
}
quotes := false
runes := []rune(s)
for idx := 0; idx < len(runes); idx++ {
r := runes[idx]
@@ -48,6 +49,10 @@ func parseCSV(s string) ([]string, error) {
add()
continue
}
if r == '=' && inKey {
inKey = false
continue
}
if r == '\\' {
if next == '\'' {
idx++
@@ -59,72 +64,60 @@ func parseCSV(s string) ([]string, error) {
if next == ',' || eof {
continue
}
return parts, fmt.Errorf("%v has an unexpected char at pos %v", s, idx)
fail("%v has an unexpected char at pos %v", s, idx)
} else {
quotes = true
continue
}
}
current = append(current, r)
if inKey {
key = append(key, r)
} else {
value = append(value, r)
}
}
if quotes {
return parts, fmt.Errorf("%v is not quoted properly", s)
fail("%v is not quoted properly", s)
}
add()
return parts, nil
return d
}
func parseTag(fv reflect.Value, s string) (*Tag, error) {
func parseTag(fv reflect.Value, s string) *Tag {
t := &Tag{}
if s == "" {
return t, nil
return t
}
parts, err := parseCSV(s)
if err != nil {
return t, err
}
for _, part := range parts {
is := func(m string) bool { return part == m }
value := func(m string) (string, bool) {
split := strings.SplitN(part, "=", 2)
if split[0] != m {
return "", false
}
if len(split) == 1 {
return "", true
}
return split[1], true
}
if is("cmd") {
for k, v := range parseCSV(s) {
switch k {
case "cmd":
t.Cmd = true
} else if is("arg") {
case "arg":
t.Arg = true
} else if is("required") {
case "required":
t.Required = true
} else if is("optional") {
case "optional":
t.Optional = true
} else if v, ok := value("default"); ok {
case "default":
t.Default = v
} else if v, ok := value("help"); ok {
case "help":
t.Help = v
} else if v, ok := value("type"); ok {
case "type":
t.Type = v
} else if v, ok := value("placeholder"); ok {
case "placeholder":
t.Placeholder = v
} else if v, ok := value("env"); ok {
case "env":
t.Env = v
} else if v, ok := value("rune"); ok {
case "rune":
t.Short, _ = utf8.DecodeRuneInString(v)
if t.Short == utf8.RuneError {
t.Short = 0
}
} else {
return t, fmt.Errorf("%v is an unknown kong key", part)
default:
fail("%v is an unknown kong key", k)
}
}
@@ -132,5 +125,5 @@ func parseTag(fv reflect.Value, s string) (*Tag, error) {
t.Placeholder = strings.ToUpper(dashedString(fv.Type().Name()))
}
return t, nil
return t
}