Add Decode() method to Value to DRY.
This commit is contained in:
+15
-5
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user