...

Source file src/dario.cat/mergo/map.go

Documentation: dario.cat/mergo

     1  // Copyright 2014 Dario Castañé. All rights reserved.
     2  // Copyright 2009 The Go Authors. All rights reserved.
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  // Based on src/pkg/reflect/deepequal.go from official
     7  // golang's stdlib.
     8  
     9  package mergo
    10  
    11  import (
    12  	"fmt"
    13  	"reflect"
    14  	"unicode"
    15  	"unicode/utf8"
    16  )
    17  
    18  func changeInitialCase(s string, mapper func(rune) rune) string {
    19  	if s == "" {
    20  		return s
    21  	}
    22  	r, n := utf8.DecodeRuneInString(s)
    23  	return string(mapper(r)) + s[n:]
    24  }
    25  
    26  func isExported(field reflect.StructField) bool {
    27  	r, _ := utf8.DecodeRuneInString(field.Name)
    28  	return r >= 'A' && r <= 'Z'
    29  }
    30  
    31  // Traverses recursively both values, assigning src's fields values to dst.
    32  // The map argument tracks comparisons that have already been seen, which allows
    33  // short circuiting on recursive types.
    34  func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
    35  	overwrite := config.Overwrite
    36  	if dst.CanAddr() {
    37  		addr := dst.UnsafeAddr()
    38  		h := 17 * addr
    39  		seen := visited[h]
    40  		typ := dst.Type()
    41  		for p := seen; p != nil; p = p.next {
    42  			if p.ptr == addr && p.typ == typ {
    43  				return nil
    44  			}
    45  		}
    46  		// Remember, remember...
    47  		visited[h] = &visit{typ, seen, addr}
    48  	}
    49  	zeroValue := reflect.Value{}
    50  	switch dst.Kind() {
    51  	case reflect.Map:
    52  		dstMap := dst.Interface().(map[string]interface{})
    53  		for i, n := 0, src.NumField(); i < n; i++ {
    54  			srcType := src.Type()
    55  			field := srcType.Field(i)
    56  			if !isExported(field) {
    57  				continue
    58  			}
    59  			fieldName := field.Name
    60  			fieldName = changeInitialCase(fieldName, unicode.ToLower)
    61  			if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v), !config.ShouldNotDereference) || overwrite) {
    62  				dstMap[fieldName] = src.Field(i).Interface()
    63  			}
    64  		}
    65  	case reflect.Ptr:
    66  		if dst.IsNil() {
    67  			v := reflect.New(dst.Type().Elem())
    68  			dst.Set(v)
    69  		}
    70  		dst = dst.Elem()
    71  		fallthrough
    72  	case reflect.Struct:
    73  		srcMap := src.Interface().(map[string]interface{})
    74  		for key := range srcMap {
    75  			config.overwriteWithEmptyValue = true
    76  			srcValue := srcMap[key]
    77  			fieldName := changeInitialCase(key, unicode.ToUpper)
    78  			dstElement := dst.FieldByName(fieldName)
    79  			if dstElement == zeroValue {
    80  				// We discard it because the field doesn't exist.
    81  				continue
    82  			}
    83  			srcElement := reflect.ValueOf(srcValue)
    84  			dstKind := dstElement.Kind()
    85  			srcKind := srcElement.Kind()
    86  			if srcKind == reflect.Ptr && dstKind != reflect.Ptr {
    87  				srcElement = srcElement.Elem()
    88  				srcKind = reflect.TypeOf(srcElement.Interface()).Kind()
    89  			} else if dstKind == reflect.Ptr {
    90  				// Can this work? I guess it can't.
    91  				if srcKind != reflect.Ptr && srcElement.CanAddr() {
    92  					srcPtr := srcElement.Addr()
    93  					srcElement = reflect.ValueOf(srcPtr)
    94  					srcKind = reflect.Ptr
    95  				}
    96  			}
    97  
    98  			if !srcElement.IsValid() {
    99  				continue
   100  			}
   101  			if srcKind == dstKind {
   102  				if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
   103  					return
   104  				}
   105  			} else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface {
   106  				if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
   107  					return
   108  				}
   109  			} else if srcKind == reflect.Map {
   110  				if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil {
   111  					return
   112  				}
   113  			} else {
   114  				return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
   115  			}
   116  		}
   117  	}
   118  	return
   119  }
   120  
   121  // Map sets fields' values in dst from src.
   122  // src can be a map with string keys or a struct. dst must be the opposite:
   123  // if src is a map, dst must be a valid pointer to struct. If src is a struct,
   124  // dst must be map[string]interface{}.
   125  // It won't merge unexported (private) fields and will do recursively
   126  // any exported field.
   127  // If dst is a map, keys will be src fields' names in lower camel case.
   128  // Missing key in src that doesn't match a field in dst will be skipped. This
   129  // doesn't apply if dst is a map.
   130  // This is separated method from Merge because it is cleaner and it keeps sane
   131  // semantics: merging equal types, mapping different (restricted) types.
   132  func Map(dst, src interface{}, opts ...func(*Config)) error {
   133  	return _map(dst, src, opts...)
   134  }
   135  
   136  // MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by
   137  // non-empty src attribute values.
   138  // Deprecated: Use Map(…) with WithOverride
   139  func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
   140  	return _map(dst, src, append(opts, WithOverride)...)
   141  }
   142  
   143  func _map(dst, src interface{}, opts ...func(*Config)) error {
   144  	if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
   145  		return ErrNonPointerArgument
   146  	}
   147  	var (
   148  		vDst, vSrc reflect.Value
   149  		err        error
   150  	)
   151  	config := &Config{}
   152  
   153  	for _, opt := range opts {
   154  		opt(config)
   155  	}
   156  
   157  	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
   158  		return err
   159  	}
   160  	// To be friction-less, we redirect equal-type arguments
   161  	// to deepMerge. Only because arguments can be anything.
   162  	if vSrc.Kind() == vDst.Kind() {
   163  		return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
   164  	}
   165  	switch vSrc.Kind() {
   166  	case reflect.Struct:
   167  		if vDst.Kind() != reflect.Map {
   168  			return ErrExpectedMapAsDestination
   169  		}
   170  	case reflect.Map:
   171  		if vDst.Kind() != reflect.Struct {
   172  			return ErrExpectedStructAsDestination
   173  		}
   174  	default:
   175  		return ErrNotSupported
   176  	}
   177  	return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config)
   178  }
   179  

View as plain text