From 1f3eace72ff414194d05d80b5597bf41c336ea3d Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Sat, 27 Jan 2018 14:31:12 +1100 Subject: [PATCH] bencode: Add ignore_unmarshal_type_error tag --- bencode/decode.go | 166 ++++++++++++++++++++++++----------------- bencode/decode_test.go | 11 +++ bencode/encode.go | 17 ++--- bencode/tags.go | 45 ++++++----- metainfo/metainfo.go | 2 +- 5 files changed, 141 insertions(+), 100 deletions(-) diff --git a/bencode/decode.go b/bencode/decode.go index 67554bcc..074db675 100644 --- a/bencode/decode.go +++ b/bencode/decode.go @@ -20,7 +20,6 @@ type Decoder struct { // Sum of bytes used to Decode values. Offset int64 buf bytes.Buffer - key string } func (d *Decoder) Decode(v interface{}) (err error) { @@ -186,6 +185,82 @@ func (d *Decoder) parseString(v reflect.Value) error { return nil } +// Info for parsing a dict value. +type dictField struct { + Value reflect.Value // Storage for the parsed value. + // True if field value should be parsed into Value. If false, the value + // should be parsed and discarded. + Ok bool + Set func() // Call this after parsing into Value. + IgnoreUnmarshalTypeError bool +} + +// Returns specifics for parsing a dict field value. +func getDictField(dict reflect.Value, key string) dictField { + // get valuev as a map value or as a struct field + switch dict.Kind() { + case reflect.Map: + value := reflect.New(dict.Type().Elem()).Elem() + return dictField{ + Value: value, + Ok: true, + Set: func() { + // Assigns the value into the map. + dict.SetMapIndex(reflect.ValueOf(key), value) + }, + } + case reflect.Struct: + sf, ok := getStructFieldForKey(dict.Type(), key) + if !ok { + return dictField{} + } + if sf.PkgPath != "" { + panic(&UnmarshalFieldError{ + Key: key, + Type: dict.Type(), + Field: sf, + }) + } + return dictField{ + Value: dict.FieldByIndex(sf.Index), + Ok: true, + Set: func() {}, + IgnoreUnmarshalTypeError: getTag(sf.Tag).IgnoreUnmarshalTypeError(), + } + default: + panic(dict.Kind()) + } +} + +func getStructFieldForKey(struct_ reflect.Type, key string) (f reflect.StructField, ok bool) { + for i, n := 0, struct_.NumField(); i < n; i++ { + f = struct_.Field(i) + tag := f.Tag.Get("bencode") + if tag == "-" { + continue + } + if f.Anonymous { + continue + } + + if parseTag(tag).Key() == key { + ok = true + break + } + + if f.Name == key { + ok = true + break + } + + if strings.EqualFold(f.Name, key) { + ok = true + break + } + } + return +} + func (d *Decoder) parseDict(v reflect.Value) error { switch v.Kind() { case reflect.Map: @@ -207,14 +282,12 @@ func (d *Decoder) parseDict(v reflect.Value) error { }) } - var mapElem reflect.Value - // so, at this point 'd' byte was consumed, let's just read key/value // pairs one by one for { - var valuev reflect.Value - keyv := reflect.ValueOf(&d.key).Elem() - ok, err := d.parseValue(keyv) + var keyStr string + keyValue := reflect.ValueOf(&keyStr).Elem() + ok, err := d.parseValue(keyValue) if err != nil { return fmt.Errorf("error parsing dict key: %s", err) } @@ -222,77 +295,30 @@ func (d *Decoder) parseDict(v reflect.Value) error { return nil } - // get valuev as a map value or as a struct field - switch v.Kind() { - case reflect.Map: - elem_type := v.Type().Elem() - if !mapElem.IsValid() { - mapElem = reflect.New(elem_type).Elem() - } else { - mapElem.Set(reflect.Zero(elem_type)) - } - valuev = mapElem - case reflect.Struct: - var f reflect.StructField - var ok bool - - t := v.Type() - for i, n := 0, t.NumField(); i < n; i++ { - f = t.Field(i) - tag := f.Tag.Get("bencode") - if tag == "-" { - continue - } - if f.Anonymous { - continue - } - - tag_name, _ := parseTag(tag) - if tag_name == d.key { - ok = true - break - } - - if f.Name == d.key { - ok = true - break - } - - if strings.EqualFold(f.Name, d.key) { - ok = true - break - } - } + df := getDictField(v, keyStr) - if ok { - if f.PkgPath != "" { - panic(&UnmarshalFieldError{ - Key: d.key, - Type: v.Type(), - Field: f, - }) - } else { - valuev = v.FieldByIndex(f.Index) - } - } else { - _, ok := d.parseValueInterface() - if !ok { - return fmt.Errorf("error parsing dict value for key %q", d.key) - } - continue + // now we need to actually parse it + if df.Ok { + // log.Printf("parsing ok struct field for key %q", keyStr) + ok, err = d.parseValue(df.Value) + } else { + // Discard the value, there's nowhere to put it. + var if_ interface{} + if_, ok = d.parseValueInterface() + if if_ == nil { + err = fmt.Errorf("error parsing value for key %q", keyStr) } } - - // now we need to actually parse it - ok, err = d.parseValue(valuev) if err != nil { - return fmt.Errorf("parsing value for key %q: %s", d.key, err) + if _, ok := err.(*UnmarshalTypeError); !ok || !df.IgnoreUnmarshalTypeError { + return fmt.Errorf("parsing value for key %q: %s", keyStr, err) + } } if !ok { - return fmt.Errorf("missing value for key %q", d.key) + return fmt.Errorf("missing value for key %q", keyStr) } - if v.Kind() == reflect.Map { - v.SetMapIndex(keyv, valuev) + if df.Ok { + df.Set() } } } diff --git a/bencode/decode_test.go b/bencode/decode_test.go index c1ccf303..fa5212b9 100644 --- a/bencode/decode_test.go +++ b/bencode/decode_test.go @@ -136,3 +136,14 @@ func TestUnmarshalerBencode(t *testing.T) { assert_equal(t, ss[2].x, "3:way") } + +func TestIgnoreUnmarshalTypeError(t *testing.T) { + s := struct { + Ignore int `bencode:",ignore_unmarshal_type_error"` + Normal int + }{} + require.Error(t, Unmarshal([]byte("d6:Normal5:helloe"), &s)) + assert.Nil(t, Unmarshal([]byte("d6:Ignore5:helloe"), &s)) + require.Nil(t, Unmarshal([]byte("d6:Ignorei42ee"), &s)) + assert.EqualValues(t, 42, s.Ignore) +} diff --git a/bencode/encode.go b/bencode/encode.go index 8cbaf29f..e364a5d3 100644 --- a/bencode/encode.go +++ b/bencode/encode.go @@ -240,17 +240,14 @@ func encodeFields(t reflect.Type) []encodeField { ef.i = i ef.tag = f.Name - tv := f.Tag.Get("bencode") - if tv != "" { - if tv == "-" { - continue - } - name, opts := parseTag(tv) - if name != "" { - ef.tag = name - } - ef.omit_empty = opts.contains("omitempty") + tv := getTag(f.Tag) + if tv.Ignore() { + continue + } + if tv.Key() != "" { + ef.tag = tv.Key() } + ef.omit_empty = tv.OmitEmpty() fs = append(fs, ef) } fss := encodeFieldsSortType(fs) diff --git a/bencode/tags.go b/bencode/tags.go index 385f3115..50bdc72b 100644 --- a/bencode/tags.go +++ b/bencode/tags.go @@ -1,34 +1,41 @@ package bencode import ( + "reflect" "strings" ) -type tagOptions string +func getTag(st reflect.StructTag) tag { + return parseTag(st.Get("bencode")) +} -func parseTag(tag string) (string, tagOptions) { - if idx := strings.Index(tag, ","); idx != -1 { - return tag[:idx], tagOptions(tag[idx+1:]) - } - return tag, tagOptions("") +type tag []string + +func parseTag(tagStr string) tag { + return strings.Split(tagStr, ",") } -func (opts tagOptions) contains(option_name string) bool { - if len(opts) == 0 { - return false - } +func (me tag) Ignore() bool { + return me[0] == "-" +} - s := string(opts) - for s != "" { - var next string - i := strings.Index(s, ",") - if i != -1 { - s, next = s[:i], s[i+1:] - } - if s == option_name { +func (me tag) Key() string { + return me[0] +} + +func (me tag) HasOpt(opt string) bool { + for _, s := range me[1:] { + if s == opt { return true } - s = next } return false } + +func (me tag) OmitEmpty() bool { + return me.HasOpt("omitempty") +} + +func (me tag) IgnoreUnmarshalTypeError() bool { + return me.HasOpt("ignore_unmarshal_type_error") +} diff --git a/metainfo/metainfo.go b/metainfo/metainfo.go index a27f4d51..62b6be19 100644 --- a/metainfo/metainfo.go +++ b/metainfo/metainfo.go @@ -13,7 +13,7 @@ type MetaInfo struct { Announce string `bencode:"announce,omitempty"` AnnounceList AnnounceList `bencode:"announce-list,omitempty"` Nodes []Node `bencode:"nodes,omitempty"` - CreationDate int64 `bencode:"creation date,omitempty"` + CreationDate int64 `bencode:"creation date,omitempty,ignore_unmarshal_type_error"` Comment string `bencode:"comment,omitempty"` CreatedBy string `bencode:"created by,omitempty"` Encoding string `bencode:"encoding,omitempty"` -- 2.44.0