Add Decode() method to Value to DRY.
This commit is contained in:
+15
-5
@@ -13,7 +13,12 @@ type DecoderContext struct {
|
|||||||
Value *Value
|
Value *Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A Decoder knows how to decode text into a Go value.
|
||||||
type Decoder interface {
|
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
|
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{}
|
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 {
|
type NamedDecoder interface {
|
||||||
Name() string
|
Name() string
|
||||||
Decoder
|
Decoder
|
||||||
@@ -98,7 +109,7 @@ func DecoderForField(field reflect.StructField) Decoder {
|
|||||||
return DecoderForType(field.Type)
|
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.
|
// Will return nil if a decoder can not be determined.
|
||||||
func DecoderForType(typ reflect.Type) Decoder {
|
func DecoderForType(typ reflect.Type) Decoder {
|
||||||
@@ -125,7 +136,7 @@ func RegisterDecoder(decoders ...Decoder) {
|
|||||||
case NamedDecoder:
|
case NamedDecoder:
|
||||||
namedDecoders[decoder.Name()] = decoder
|
namedDecoders[decoder.Name()] = decoder
|
||||||
default:
|
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
|
var childScanner *Scanner
|
||||||
if ctx.Value.Flag {
|
if ctx.Value.Flag {
|
||||||
|
// If decoding a flag, we need an argument.
|
||||||
childScanner = Scan(strings.Split(scan.PopValue("list"), sep)...)
|
childScanner = Scan(strings.Split(scan.PopValue("list"), sep)...)
|
||||||
} else {
|
} else {
|
||||||
tokens := scan.PopUntil(func(t Token) bool {
|
tokens := scan.PopUntil(func(t Token) bool { return !t.IsValue() })
|
||||||
return !t.IsValue() || (t.Type == UntypedToken && strings.HasPrefix(t.Value, "-"))
|
|
||||||
})
|
|
||||||
childScanner = Scan(tokens...)
|
childScanner = Scan(tokens...)
|
||||||
}
|
}
|
||||||
childDecoder := DecoderForType(el)
|
childDecoder := DecoderForType(el)
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func (k *Kong) reset(node *Node) {
|
|||||||
for _, flag := range node.Flags {
|
for _, flag := range node.Flags {
|
||||||
flag.Value.Value.Set(reflect.Zero(flag.Value.Value.Type()))
|
flag.Value.Value.Set(reflect.Zero(flag.Value.Value.Type()))
|
||||||
if flag.Default != "" {
|
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 {
|
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.
|
// Ensure we've consumed all positional arguments.
|
||||||
if positional < len(node.Positional) {
|
if positional < len(node.Positional) {
|
||||||
arg := node.Positional[positional]
|
arg := node.Positional[positional]
|
||||||
err := arg.Decoder.Decode(&DecoderContext{Value: arg}, scan, arg.Value)
|
err := arg.Decode(scan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -169,7 +169,7 @@ func (k *Kong) applyNode(scan *Scanner, node *Node) (command []string, err error
|
|||||||
|
|
||||||
case branch.Argument != nil:
|
case branch.Argument != nil:
|
||||||
arg := branch.Argument.Argument
|
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+">")
|
command = append(command, "<"+arg.Name+">")
|
||||||
cmd, err := k.applyNode(scan, &branch.Argument.Node)
|
cmd, err := k.applyNode(scan, &branch.Argument.Node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -200,7 +200,7 @@ func matchFlags(flags []*Flag, token Token, scan *Scanner, matcher func(f *Flag)
|
|||||||
for _, flag := range flags {
|
for _, flag := range flags {
|
||||||
// Found a matching flag.
|
// Found a matching flag.
|
||||||
if flag.Name == token.Value {
|
if flag.Name == token.Value {
|
||||||
err := flag.Decoder.Decode(&DecoderContext{Value: &flag.Value}, scan, flag.Value.Value)
|
err := flag.Decode(scan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ type Value struct {
|
|||||||
Format string // Formatting directive, if applicable.
|
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 Positional = Value
|
||||||
|
|
||||||
type Argument struct {
|
type Argument struct {
|
||||||
|
|||||||
+7
-2
@@ -2,6 +2,7 @@ package kong
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate stringer -type=TokenType
|
//go:generate stringer -type=TokenType
|
||||||
@@ -48,8 +49,12 @@ func (t Token) IsAny(types ...TokenType) bool {
|
|||||||
return false
|
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 {
|
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 {
|
type Scanner struct {
|
||||||
@@ -77,7 +82,7 @@ func (s *Scanner) Pop() Token {
|
|||||||
return arg
|
return arg
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopValue or panic with Error.
|
// PopValue token, or panic with Error.
|
||||||
func (s *Scanner) PopValue(context string) string {
|
func (s *Scanner) PopValue(context string) string {
|
||||||
t := s.Pop()
|
t := s.Pop()
|
||||||
if !t.IsValue() {
|
if !t.IsValue() {
|
||||||
|
|||||||
Reference in New Issue
Block a user