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:
committed by
Alec Thomas
parent
31e1ffaa3b
commit
ecd369e622
@@ -61,11 +61,7 @@ func buildNode(v reflect.Value, seenFlags map[string]bool, cmd bool) *Node {
|
|||||||
name = strings.ToLower(dashedString(ft.Name))
|
name = strings.ToLower(dashedString(ft.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
tag, err := parseTag(fv, ft.Tag.Get("kong"))
|
tag := parseTag(fv, ft.Tag.Get("kong"))
|
||||||
if err != nil {
|
|
||||||
fail("%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder := DecoderForField(tag.Type, ft)
|
decoder := DecoderForField(tag.Type, ft)
|
||||||
|
|
||||||
if !cmd {
|
if !cmd {
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package kong
|
package kong
|
||||||
|
|
||||||
import "os"
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
// Parse constructs a new parser and parses the default command-line.
|
// Parse constructs a new parser and parses the default command-line.
|
||||||
func Parse(cli interface{}, options ...Option) string {
|
func Parse(cli interface{}, options ...Option) string {
|
||||||
parser, err := New(cli, options...)
|
parser, err := New(cli, options...)
|
||||||
parser.FatalIfErrorf(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
cmd, err := parser.Parse(os.Args[1:])
|
cmd, err := parser.Parse(os.Args[1:])
|
||||||
parser.FatalIfErrorf(err)
|
parser.FatalIfErrorf(err)
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
@@ -51,6 +51,7 @@ func New(ast interface{}, options ...Option) (*Kong, error) {
|
|||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
option(k)
|
option(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
return k, nil
|
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{}) {
|
func (k *Kong) Errorf(format string, args ...interface{}) {
|
||||||
if k.Model != nil {
|
fmt.Fprintf(os.Stderr, k.Model.Name+": "+format, args...)
|
||||||
fmt.Fprintf(os.Stderr, k.Model.Name+": "+format, args...)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(os.Stderr, format, args...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Kong) FatalIfErrorf(err error, args ...interface{}) {
|
func (k *Kong) FatalIfErrorf(err error, args ...interface{}) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package kong
|
package kong
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
@@ -21,19 +20,21 @@ type Tag struct {
|
|||||||
Short rune
|
Short rune
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCSV(s string) ([]string, error) {
|
func parseCSV(s string) map[string]string {
|
||||||
num := 0
|
d := map[string]string{}
|
||||||
parts := []string{}
|
|
||||||
current := []rune{}
|
key := []rune{}
|
||||||
|
value := []rune{}
|
||||||
|
quotes := false
|
||||||
|
inKey := true
|
||||||
|
|
||||||
add := func() {
|
add := func() {
|
||||||
parts = append(parts, string(current))
|
d[string(key)] = string(value)
|
||||||
current = []rune{}
|
key = []rune{}
|
||||||
num++
|
value = []rune{}
|
||||||
|
inKey = true
|
||||||
}
|
}
|
||||||
|
|
||||||
quotes := false
|
|
||||||
|
|
||||||
runes := []rune(s)
|
runes := []rune(s)
|
||||||
for idx := 0; idx < len(runes); idx++ {
|
for idx := 0; idx < len(runes); idx++ {
|
||||||
r := runes[idx]
|
r := runes[idx]
|
||||||
@@ -48,6 +49,10 @@ func parseCSV(s string) ([]string, error) {
|
|||||||
add()
|
add()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if r == '=' && inKey {
|
||||||
|
inKey = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
if r == '\\' {
|
if r == '\\' {
|
||||||
if next == '\'' {
|
if next == '\'' {
|
||||||
idx++
|
idx++
|
||||||
@@ -59,72 +64,60 @@ func parseCSV(s string) ([]string, error) {
|
|||||||
if next == ',' || eof {
|
if next == ',' || eof {
|
||||||
continue
|
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 {
|
} else {
|
||||||
quotes = true
|
quotes = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
current = append(current, r)
|
if inKey {
|
||||||
|
key = append(key, r)
|
||||||
|
} else {
|
||||||
|
value = append(value, r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if quotes {
|
if quotes {
|
||||||
return parts, fmt.Errorf("%v is not quoted properly", s)
|
fail("%v is not quoted properly", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
add()
|
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{}
|
t := &Tag{}
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return t, nil
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
parts, err := parseCSV(s)
|
for k, v := range parseCSV(s) {
|
||||||
if err != nil {
|
switch k {
|
||||||
return t, err
|
case "cmd":
|
||||||
}
|
|
||||||
|
|
||||||
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") {
|
|
||||||
t.Cmd = true
|
t.Cmd = true
|
||||||
} else if is("arg") {
|
case "arg":
|
||||||
t.Arg = true
|
t.Arg = true
|
||||||
} else if is("required") {
|
case "required":
|
||||||
t.Required = true
|
t.Required = true
|
||||||
} else if is("optional") {
|
case "optional":
|
||||||
t.Optional = true
|
t.Optional = true
|
||||||
} else if v, ok := value("default"); ok {
|
case "default":
|
||||||
t.Default = v
|
t.Default = v
|
||||||
} else if v, ok := value("help"); ok {
|
case "help":
|
||||||
t.Help = v
|
t.Help = v
|
||||||
} else if v, ok := value("type"); ok {
|
case "type":
|
||||||
t.Type = v
|
t.Type = v
|
||||||
} else if v, ok := value("placeholder"); ok {
|
case "placeholder":
|
||||||
t.Placeholder = v
|
t.Placeholder = v
|
||||||
} else if v, ok := value("env"); ok {
|
case "env":
|
||||||
t.Env = v
|
t.Env = v
|
||||||
} else if v, ok := value("rune"); ok {
|
case "rune":
|
||||||
t.Short, _ = utf8.DecodeRuneInString(v)
|
t.Short, _ = utf8.DecodeRuneInString(v)
|
||||||
if t.Short == utf8.RuneError {
|
if t.Short == utf8.RuneError {
|
||||||
t.Short = 0
|
t.Short = 0
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
return t, fmt.Errorf("%v is an unknown kong key", part)
|
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()))
|
t.Placeholder = strings.ToUpper(dashedString(fv.Type().Name()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return t, nil
|
return t
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user