]> Sergey Matveev's repositories - btrtrc.git/commitdiff
bencode: Add ignore_unmarshal_type_error tag
authorMatt Joiner <anacrolix@gmail.com>
Sat, 27 Jan 2018 03:31:12 +0000 (14:31 +1100)
committerMatt Joiner <anacrolix@gmail.com>
Sat, 27 Jan 2018 03:31:46 +0000 (14:31 +1100)
bencode/decode.go
bencode/decode_test.go
bencode/encode.go
bencode/tags.go
metainfo/metainfo.go

index 67554bcc8b68b0ab8d2327ca71dd2086e3cb20d0..074db675301d253fec1b750719ddefaa8ea10b9c 100644 (file)
@@ -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()
                }
        }
 }
index c1ccf3031c4d1319f87e4e418eb6a4d85df6b4c1..fa5212b9f704620899a7c3cd55eacf88e94ad768 100644 (file)
@@ -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)
+}
index 8cbaf29f1f1435a8f4764c12ba794a0e8eb60e94..e364a5d317cb958a514466f98182d37fd210bd5a 100644 (file)
@@ -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)
index 385f3115f76b5509f062200d56028850d5ee844f..50bdc72b74e1e1cd24480fd5af849066c3ff6570 100644 (file)
@@ -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")
+}
index a27f4d515199fd6a47512ec675d45d63e9d2fdee..62b6be1958a0425baa356692eb8b9d0bbd1d8ad3 100644 (file)
@@ -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"`