Add Decode() method to Value to DRY.

This commit is contained in:
Alec Thomas
2018-05-17 23:50:12 +10:00
parent 31fe51f9d8
commit f929749094
4 changed files with 30 additions and 11 deletions
+15 -5
View File
@@ -13,7 +13,12 @@ type DecoderContext struct {
Value *Value
}
// A Decoder knows how to decode text into a Go value.
type Decoder interface {
// Decode scan into target.
//
// "ctx" contains context about the value being decoded that may be useful
// to some decoders.
Decode(ctx *DecoderContext, scan *Scanner, target reflect.Value) error
}
@@ -61,6 +66,12 @@ func (k *kindDecoder) Kind() reflect.Kind { return k.kind }
var _ KindDecoder = &kindDecoder{}
// A NamedDecoder will be used if the value field has a "type" tag matching Name().
//
// eg.
//
// Field string `type:"colour"`
// kong.RegisterDecoder(kong.NewNamedDecoder("colour", ...))
type NamedDecoder interface {
Name() string
Decoder
@@ -98,7 +109,7 @@ func DecoderForField(field reflect.StructField) Decoder {
return DecoderForType(field.Type)
}
// DecoderForType finds a decoder via a type or kind.
// DecoderForType finds a decoder from a type or kind.
//
// Will return nil if a decoder can not be determined.
func DecoderForType(typ reflect.Type) Decoder {
@@ -125,7 +136,7 @@ func RegisterDecoder(decoders ...Decoder) {
case NamedDecoder:
namedDecoders[decoder.Name()] = decoder
default:
fail("unsupported decoder type " + reflect.TypeOf(decoder).String())
fail("unsupported decoder type %T", decoder)
}
}
}
@@ -220,11 +231,10 @@ func sliceDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) erro
}
var childScanner *Scanner
if ctx.Value.Flag {
// If decoding a flag, we need an argument.
childScanner = Scan(strings.Split(scan.PopValue("list"), sep)...)
} else {
tokens := scan.PopUntil(func(t Token) bool {
return !t.IsValue() || (t.Type == UntypedToken && strings.HasPrefix(t.Value, "-"))
})
tokens := scan.PopUntil(func(t Token) bool { return !t.IsValue() })
childScanner = Scan(tokens...)
}
childDecoder := DecoderForType(el)
+4 -4
View File
@@ -61,7 +61,7 @@ func (k *Kong) reset(node *Node) {
for _, flag := range node.Flags {
flag.Value.Value.Set(reflect.Zero(flag.Value.Value.Type()))
if flag.Default != "" {
flag.Decoder.Decode(&DecoderContext{Value: &flag.Value}, Scan(flag.Default), flag.Value.Value)
flag.Decode(Scan(flag.Default))
}
}
for _, pos := range node.Positional {
@@ -144,7 +144,7 @@ func (k *Kong) applyNode(scan *Scanner, node *Node) (command []string, err error
// Ensure we've consumed all positional arguments.
if positional < len(node.Positional) {
arg := node.Positional[positional]
err := arg.Decoder.Decode(&DecoderContext{Value: arg}, scan, arg.Value)
err := arg.Decode(scan)
if err != nil {
return nil, err
}
@@ -169,7 +169,7 @@ func (k *Kong) applyNode(scan *Scanner, node *Node) (command []string, err error
case branch.Argument != nil:
arg := branch.Argument.Argument
if err := arg.Decoder.Decode(&DecoderContext{Value: arg}, scan, arg.Value); err == nil {
if err := arg.Decode(scan); err == nil {
command = append(command, "<"+arg.Name+">")
cmd, err := k.applyNode(scan, &branch.Argument.Node)
if err != nil {
@@ -200,7 +200,7 @@ func matchFlags(flags []*Flag, token Token, scan *Scanner, matcher func(f *Flag)
for _, flag := range flags {
// Found a matching flag.
if flag.Name == token.Value {
err := flag.Decoder.Decode(&DecoderContext{Value: &flag.Value}, scan, flag.Value.Value)
err := flag.Decode(scan)
if err != nil {
return err
}
+4
View File
@@ -31,6 +31,10 @@ type Value struct {
Format string // Formatting directive, if applicable.
}
func (v *Value) Decode(scan *Scanner) error {
return v.Decoder.Decode(&DecoderContext{Value: v}, scan, v.Value)
}
type Positional = Value
type Argument struct {
+7 -2
View File
@@ -2,6 +2,7 @@ package kong
import (
"strconv"
"strings"
)
//go:generate stringer -type=TokenType
@@ -48,8 +49,12 @@ func (t Token) IsAny(types ...TokenType) bool {
return false
}
// IsValue returns true if token is usable as a parseable value.
//
// A parseable value is either a value typed token, or an untyped token NOT starting with a hyphen.
func (t Token) IsValue() bool {
return t.IsAny(FlagValueToken, ShortFlagTailToken, PositionalArgumentToken, UntypedToken)
return t.IsAny(FlagValueToken, ShortFlagTailToken, PositionalArgumentToken) ||
(t.Type == UntypedToken && !strings.HasPrefix(t.Value, "-"))
}
type Scanner struct {
@@ -77,7 +82,7 @@ func (s *Scanner) Pop() Token {
return arg
}
// PopValue or panic with Error.
// PopValue token, or panic with Error.
func (s *Scanner) PopValue(context string) string {
t := s.Pop()
if !t.IsValue() {