From cb88963909be7f7f98954d083d08fd17517831dd Mon Sep 17 00:00:00 2001 From: Alec Thomas Date: Thu, 17 May 2018 11:21:20 +1000 Subject: [PATCH] Clean up decoders a bit. --- build.go | 5 +- decoders.go | 137 ++++++++++++++++++++++++++-------------------------- 2 files changed, 72 insertions(+), 70 deletions(-) diff --git a/build.go b/build.go index 7a8dccd..2e38444 100644 --- a/build.go +++ b/build.go @@ -37,7 +37,10 @@ func buildNode(v reflect.Value) *Node { name = strings.ToLower(strings.Join(camelCase(ft.Name), "-")) } help := ft.Tag.Get("help") - decoder := DecoderForField(ft) + decoder, err := DecoderForField(ft) + if err != nil && ft.Type.Kind() != reflect.Struct { + panic(err) + } dflt := ft.Tag.Get("default") placeholder := ft.Tag.Get("placeholder") if placeholder == "" { diff --git a/decoders.go b/decoders.go index 26ddfca..88c5dbd 100644 --- a/decoders.go +++ b/decoders.go @@ -85,26 +85,25 @@ var ( ) // DecoderForField finds a decoder for a struct field. -// -func DecoderForField(field reflect.StructField) Decoder { +func DecoderForField(field reflect.StructField) (Decoder, error) { name, ok := field.Tag.Lookup("type") if ok { if decoder, ok := namedDecoders[name]; ok { - return decoder + return decoder, nil } } return DecoderForType(field.Type) } -func DecoderForType(typ reflect.Type) Decoder { +func DecoderForType(typ reflect.Type) (Decoder, error) { var decoder Decoder var ok bool if decoder, ok = typeDecoders[typ]; ok { - return decoder + return decoder, nil } else if decoder, ok = kindDecoders[typ.Kind()]; ok { - return decoder + return decoder, nil } - return missingDecoder + return nil, fmt.Errorf("no decoder for type %s", typ) } // RegisterDecoder registers decoders. @@ -126,49 +125,19 @@ func RegisterDecoder(decoders ...Decoder) { } func init() { - intDecoder := NewKindDecoder(reflect.Int, func(ctx *DecoderContext, scan *Scanner, target reflect.Value) error { - n, err := strconv.ParseInt(scan.PopValue("int"), 10, 64) - if err != nil { - return err - } - target.SetInt(n) - return nil - }) - uintDecoder := NewKindDecoder(reflect.Uint, func(ctx *DecoderContext, scan *Scanner, target reflect.Value) error { - n, err := strconv.ParseUint(scan.PopValue("uint"), 10, 64) - if err != nil { - return err - } - target.SetUint(n) - return nil - }) kindDecoders = map[reflect.Kind]KindDecoder{ - reflect.Int: intDecoder, - reflect.Int8: intDecoder, - reflect.Int16: intDecoder, - reflect.Int32: intDecoder, - reflect.Int64: intDecoder, - reflect.Uint: uintDecoder, - reflect.Uint8: uintDecoder, - reflect.Uint16: uintDecoder, - reflect.Uint32: uintDecoder, - reflect.Uint64: uintDecoder, - reflect.Float32: NewKindDecoder(reflect.Float32, func(ctx *DecoderContext, scan *Scanner, target reflect.Value) error { - n, err := strconv.ParseFloat(scan.PopValue("float"), 32) - if err != nil { - return err - } - target.SetFloat(n) - return nil - }), - reflect.Float64: NewKindDecoder(reflect.Float64, func(ctx *DecoderContext, scan *Scanner, target reflect.Value) error { - n, err := strconv.ParseFloat(scan.PopValue("float"), 64) - if err != nil { - return err - } - target.SetFloat(n) - return nil - }), + reflect.Int: NewKindDecoder(reflect.Int, intDecoder), + reflect.Int8: NewKindDecoder(reflect.Int8, intDecoder), + reflect.Int16: NewKindDecoder(reflect.Int16, intDecoder), + reflect.Int32: NewKindDecoder(reflect.Int32, intDecoder), + reflect.Int64: NewKindDecoder(reflect.Int64, intDecoder), + reflect.Uint: NewKindDecoder(reflect.Uint, uintDecoder), + reflect.Uint8: NewKindDecoder(reflect.Uint8, uintDecoder), + reflect.Uint16: NewKindDecoder(reflect.Uint16, uintDecoder), + reflect.Uint32: NewKindDecoder(reflect.Uint32, uintDecoder), + reflect.Uint64: NewKindDecoder(reflect.Uint64, uintDecoder), + reflect.Float32: NewKindDecoder(reflect.Float32, floatDecoder(32)), + reflect.Float64: NewKindDecoder(reflect.Float64, floatDecoder(64)), reflect.String: NewKindDecoder(reflect.String, func(ctx *DecoderContext, scan *Scanner, target reflect.Value) error { target.SetString(scan.PopValue("string")) return nil @@ -177,27 +146,57 @@ func init() { target.SetBool(true) return nil }), - reflect.Slice: NewKindDecoder(reflect.Slice, func(ctx *DecoderContext, scan *Scanner, target reflect.Value) error { - el := target.Type().Elem() - sep, ok := ctx.Value.Field.Tag.Lookup("sep") - if !ok { - sep = "," - } - childScanner := Scan(strings.Split(scan.PopValue("slice"), sep)...) - childDecoder := DecoderForType(el) - for childScanner.Peek().Type != EOLToken { - childValue := reflect.New(el).Elem() - err := childDecoder.Decode(ctx, childScanner, childValue) - if err != nil { - return err - } - target.Set(reflect.Append(target, childValue)) - } - return nil - }), + reflect.Slice: NewKindDecoder(reflect.Slice, sliceDecoder), } } -var missingDecoder DecoderFunc = func(ctx *DecoderContext, scan *Scanner, target reflect.Value) error { - return fmt.Errorf("no decoder for %q (of type %T) for field %q", target.String(), target.Type(), ctx.Value.Field.Name) +func intDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) error { + n, err := strconv.ParseInt(scan.PopValue("int"), 10, 64) + if err != nil { + return err + } + target.SetInt(n) + return nil +} + +func uintDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) error { + n, err := strconv.ParseUint(scan.PopValue("uint"), 10, 64) + if err != nil { + return err + } + target.SetUint(n) + return nil +} + +func floatDecoder(bits int) DecoderFunc { + return func(ctx *DecoderContext, scan *Scanner, target reflect.Value) error { + n, err := strconv.ParseFloat(scan.PopValue("float"), bits) + if err != nil { + return err + } + target.SetFloat(n) + return nil + } +} + +func sliceDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) error { + el := target.Type().Elem() + sep, ok := ctx.Value.Field.Tag.Lookup("sep") + if !ok { + sep = "," + } + childScanner := Scan(strings.Split(scan.PopValue("list"), sep)...) + childDecoder, err := DecoderForType(el) + if err != nil { + return err + } + for childScanner.Peek().Type != EOLToken { + childValue := reflect.New(el).Elem() + err := childDecoder.Decode(ctx, childScanner, childValue) + if err != nil { + return err + } + target.Set(reflect.Append(target, childValue)) + } + return nil }