diff options
Diffstat (limited to 'vendor/github.com/jinzhu/copier/copier.go')
-rw-r--r-- | vendor/github.com/jinzhu/copier/copier.go | 174 |
1 files changed, 141 insertions, 33 deletions
diff --git a/vendor/github.com/jinzhu/copier/copier.go b/vendor/github.com/jinzhu/copier/copier.go index 72bf65c78..412ff5497 100644 --- a/vendor/github.com/jinzhu/copier/copier.go +++ b/vendor/github.com/jinzhu/copier/copier.go @@ -3,9 +3,11 @@ package copier import ( "database/sql" "database/sql/driver" + "errors" "fmt" "reflect" "strings" + "unicode" ) // These flags define options for tag handling @@ -32,6 +34,19 @@ type Option struct { DeepCopy bool } +// Tag Flags +type flags struct { + BitFlags map[string]uint8 + SrcNames tagNameMapping + DestNames tagNameMapping +} + +// Field Tag name mapping +type tagNameMapping struct { + FieldNameToTag map[string]string + TagToFieldName map[string]string +} + // Copy copy things func Copy(toValue interface{}, fromValue interface{}) (err error) { return copier(toValue, fromValue, Option{}) @@ -134,7 +149,8 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) } if !set(to.Index(i), from.Index(i), opt.DeepCopy) { - err = CopyWithOption(to.Index(i).Addr().Interface(), from.Index(i).Interface(), opt) + // ignore error while copy slice element + err = copier(to.Index(i).Addr().Interface(), from.Index(i).Interface(), opt) if err != nil { continue } @@ -148,7 +164,7 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) return } - if to.Kind() == reflect.Slice { + if from.Kind() == reflect.Slice || to.Kind() == reflect.Slice { isSlice = true if from.Kind() == reflect.Slice { amount = from.Len() @@ -180,9 +196,9 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) } // Get tag options - tagBitFlags := map[string]uint8{} - if dest.IsValid() { - tagBitFlags = getBitFlags(toType) + flgs, err := getFlags(dest, source, toType, fromType) + if err != nil { + return err } // check source @@ -193,17 +209,18 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) name := field.Name // Get bit flags for field - fieldFlags, _ := tagBitFlags[name] + fieldFlags, _ := flgs.BitFlags[name] // Check if we should ignore copying if (fieldFlags & tagIgnore) != 0 { continue } - if fromField := source.FieldByName(name); fromField.IsValid() && !shouldIgnore(fromField, opt.IgnoreEmpty) { + srcFieldName, destFieldName := getFieldName(name, flgs) + if fromField := source.FieldByName(srcFieldName); fromField.IsValid() && !shouldIgnore(fromField, opt.IgnoreEmpty) { // process for nested anonymous field destFieldNotSet := false - if f, ok := dest.Type().FieldByName(name); ok { + if f, ok := dest.Type().FieldByName(destFieldName); ok { for idx := range f.Index { destField := dest.FieldByIndex(f.Index[:idx+1]) @@ -229,7 +246,7 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) break } - toField := dest.FieldByName(name) + toField := dest.FieldByName(destFieldName) if toField.IsValid() { if toField.CanSet() { if !set(toField, fromField, opt.DeepCopy) { @@ -239,16 +256,16 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) } if fieldFlags != 0 { // Note that a copy was made - tagBitFlags[name] = fieldFlags | hasCopied + flgs.BitFlags[name] = fieldFlags | hasCopied } } } else { // try to set to method var toMethod reflect.Value if dest.CanAddr() { - toMethod = dest.Addr().MethodByName(name) + toMethod = dest.Addr().MethodByName(destFieldName) } else { - toMethod = dest.MethodByName(name) + toMethod = dest.MethodByName(destFieldName) } if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) { @@ -261,16 +278,17 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) // Copy from from method to dest field for _, field := range deepFields(toType) { name := field.Name + srcFieldName, destFieldName := getFieldName(name, flgs) var fromMethod reflect.Value if source.CanAddr() { - fromMethod = source.Addr().MethodByName(name) + fromMethod = source.Addr().MethodByName(srcFieldName) } else { - fromMethod = source.MethodByName(name) + fromMethod = source.MethodByName(srcFieldName) } if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 && !shouldIgnore(fromMethod, opt.IgnoreEmpty) { - if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() { + if toField := dest.FieldByName(destFieldName); toField.IsValid() && toField.CanSet() { values := fromMethod.Call([]reflect.Value{}) if len(values) >= 1 { set(toField, values[0], opt.DeepCopy) @@ -280,25 +298,37 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) } } - if isSlice { + if isSlice && to.Kind() == reflect.Slice { if dest.Addr().Type().AssignableTo(to.Type().Elem()) { if to.Len() < i+1 { to.Set(reflect.Append(to, dest.Addr())) } else { - set(to.Index(i), dest.Addr(), opt.DeepCopy) + if !set(to.Index(i), dest.Addr(), opt.DeepCopy) { + // ignore error while copy slice element + err = copier(to.Index(i).Addr().Interface(), dest.Addr().Interface(), opt) + if err != nil { + continue + } + } } } else if dest.Type().AssignableTo(to.Type().Elem()) { if to.Len() < i+1 { to.Set(reflect.Append(to, dest)) } else { - set(to.Index(i), dest, opt.DeepCopy) + if !set(to.Index(i), dest, opt.DeepCopy) { + // ignore error while copy slice element + err = copier(to.Index(i).Addr().Interface(), dest.Interface(), opt) + if err != nil { + continue + } + } } } } else if initDest { to.Set(dest) } - err = checkBitFlags(tagBitFlags) + err = checkBitFlags(flgs.BitFlags) } return @@ -432,46 +462,90 @@ func set(to, from reflect.Value, deepCopy bool) bool { } // parseTags Parses struct tags and returns uint8 bit flags. -func parseTags(tag string) (flags uint8) { +func parseTags(tag string) (flg uint8, name string, err error) { for _, t := range strings.Split(tag, ",") { switch t { case "-": - flags = tagIgnore + flg = tagIgnore return case "must": - flags = flags | tagMust + flg = flg | tagMust case "nopanic": - flags = flags | tagNoPanic + flg = flg | tagNoPanic + default: + if unicode.IsUpper([]rune(t)[0]) { + name = strings.TrimSpace(t) + } else { + err = errors.New("copier field name tag must be start upper case") + } } } return } -// getBitFlags Parses struct tags for bit flags. -func getBitFlags(toType reflect.Type) map[string]uint8 { - flags := map[string]uint8{} - toTypeFields := deepFields(toType) +// getTagFlags Parses struct tags for bit flags, field name. +func getFlags(dest, src reflect.Value, toType, fromType reflect.Type) (flags, error) { + flgs := flags{ + BitFlags: map[string]uint8{}, + SrcNames: tagNameMapping{ + FieldNameToTag: map[string]string{}, + TagToFieldName: map[string]string{}, + }, + DestNames: tagNameMapping{ + FieldNameToTag: map[string]string{}, + TagToFieldName: map[string]string{}, + }, + } + var toTypeFields, fromTypeFields []reflect.StructField + if dest.IsValid() { + toTypeFields = deepFields(toType) + } + if src.IsValid() { + fromTypeFields = deepFields(fromType) + } // Get a list dest of tags for _, field := range toTypeFields { tags := field.Tag.Get("copier") if tags != "" { - flags[field.Name] = parseTags(tags) + var name string + var err error + if flgs.BitFlags[field.Name], name, err = parseTags(tags); err != nil { + return flags{}, err + } else if name != "" { + flgs.DestNames.FieldNameToTag[field.Name] = name + flgs.DestNames.TagToFieldName[name] = field.Name + } + } + } + + // Get a list source of tags + for _, field := range fromTypeFields { + tags := field.Tag.Get("copier") + if tags != "" { + var name string + var err error + if _, name, err = parseTags(tags); err != nil { + return flags{}, err + } else if name != "" { + flgs.SrcNames.FieldNameToTag[field.Name] = name + flgs.SrcNames.TagToFieldName[name] = field.Name + } } } - return flags + return flgs, nil } // checkBitFlags Checks flags for error or panic conditions. func checkBitFlags(flagsList map[string]uint8) (err error) { // Check flag conditions were met - for name, flags := range flagsList { - if flags&hasCopied == 0 { + for name, flgs := range flagsList { + if flgs&hasCopied == 0 { switch { - case flags&tagMust != 0 && flags&tagNoPanic != 0: + case flgs&tagMust != 0 && flgs&tagNoPanic != 0: err = fmt.Errorf("field %s has must tag but was not copied", name) return - case flags&(tagMust) != 0: + case flgs&(tagMust) != 0: panic(fmt.Sprintf("Field %s has must tag but was not copied", name)) } } @@ -479,6 +553,40 @@ func checkBitFlags(flagsList map[string]uint8) (err error) { return } +func getFieldName(fieldName string, flgs flags) (srcFieldName string, destFieldName string) { + // get dest field name + if srcTagName, ok := flgs.SrcNames.FieldNameToTag[fieldName]; ok { + destFieldName = srcTagName + if destTagName, ok := flgs.DestNames.TagToFieldName[srcTagName]; ok { + destFieldName = destTagName + } + } else { + if destTagName, ok := flgs.DestNames.TagToFieldName[fieldName]; ok { + destFieldName = destTagName + } + } + if destFieldName == "" { + destFieldName = fieldName + } + + // get source field name + if destTagName, ok := flgs.DestNames.FieldNameToTag[fieldName]; ok { + srcFieldName = destTagName + if srcField, ok := flgs.SrcNames.TagToFieldName[destTagName]; ok { + srcFieldName = srcField + } + } else { + if srcField, ok := flgs.SrcNames.TagToFieldName[fieldName]; ok { + srcFieldName = srcField + } + } + + if srcFieldName == "" { + srcFieldName = fieldName + } + return +} + func driverValuer(v reflect.Value) (i driver.Valuer, ok bool) { if !v.CanAddr() { |