github.com/bensooraj/mattermost-server@v5.11.1+incompatible/utils/merge.go (about)

     1  // Copyright (c) 2019-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package utils
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  )
    10  
    11  // StructFieldFilter defines a callback function used to decide if a patch value should be applied.
    12  type StructFieldFilter func(structField reflect.StructField, base reflect.Value, patch reflect.Value) bool
    13  
    14  // MergeConfig allows for optional merge customizations.
    15  type MergeConfig struct {
    16  	StructFieldFilter StructFieldFilter
    17  }
    18  
    19  // Merge will return a new value of the same type as base and patch, recursively merging non-nil values from patch on top of base.
    20  //
    21  // Restrictions/guarantees:
    22  //   - base and patch must be the same type
    23  //   - base and patch will never be modified
    24  //   - values from patch are always selected when non-nil
    25  //   - structs are merged recursively
    26  //   - maps and slices are treated as pointers, and merged as a single value
    27  //
    28  // Note that callers need to cast the returned interface back into the original type:
    29  // func mergeTestStruct(base, patch *testStruct) (*testStruct, error) {
    30  //     ret, err := merge(base, patch)
    31  //     if err != nil {
    32  //         return nil, err
    33  //     }
    34  //
    35  //     retTS := ret.(testStruct)
    36  //     return &retTS, nil
    37  // }
    38  func Merge(base interface{}, patch interface{}, mergeConfig *MergeConfig) (interface{}, error) {
    39  	if reflect.TypeOf(base) != reflect.TypeOf(patch) {
    40  		return nil, fmt.Errorf(
    41  			"cannot merge different types. base type: %s, patch type: %s",
    42  			reflect.TypeOf(base),
    43  			reflect.TypeOf(patch),
    44  		)
    45  	}
    46  
    47  	commonType := reflect.TypeOf(base)
    48  	baseVal := reflect.ValueOf(base)
    49  	patchVal := reflect.ValueOf(patch)
    50  	if commonType.Kind() == reflect.Ptr {
    51  		commonType = commonType.Elem()
    52  		baseVal = baseVal.Elem()
    53  		patchVal = patchVal.Elem()
    54  	}
    55  
    56  	ret := reflect.New(commonType)
    57  
    58  	val, ok := merge(baseVal, patchVal, mergeConfig)
    59  	if ok {
    60  		ret.Elem().Set(val)
    61  	}
    62  	return ret.Elem().Interface(), nil
    63  }
    64  
    65  // merge recursively merges patch into base and returns the new struct, ptr, slice/map, or value
    66  func merge(base, patch reflect.Value, mergeConfig *MergeConfig) (reflect.Value, bool) {
    67  	commonType := base.Type()
    68  
    69  	switch commonType.Kind() {
    70  	case reflect.Struct:
    71  		merged := reflect.New(commonType).Elem()
    72  		for i := 0; i < base.NumField(); i++ {
    73  			if !merged.Field(i).CanSet() {
    74  				continue
    75  			}
    76  			if mergeConfig != nil && mergeConfig.StructFieldFilter != nil {
    77  				if !mergeConfig.StructFieldFilter(commonType.Field(i), base.Field(i), patch.Field(i)) {
    78  					merged.Field(i).Set(base.Field(i))
    79  					continue
    80  				}
    81  			}
    82  			val, ok := merge(base.Field(i), patch.Field(i), mergeConfig)
    83  			if ok {
    84  				merged.Field(i).Set(val)
    85  			}
    86  		}
    87  		return merged, true
    88  
    89  	case reflect.Ptr:
    90  		mergedPtr := reflect.New(commonType.Elem())
    91  		if base.IsNil() && patch.IsNil() {
    92  			return mergedPtr, false
    93  		}
    94  
    95  		// clone reference values (if any)
    96  		if base.IsNil() {
    97  			val, _ := merge(patch.Elem(), patch.Elem(), mergeConfig)
    98  			mergedPtr.Elem().Set(val)
    99  		} else if patch.IsNil() {
   100  			val, _ := merge(base.Elem(), base.Elem(), mergeConfig)
   101  			mergedPtr.Elem().Set(val)
   102  		} else {
   103  			val, _ := merge(base.Elem(), patch.Elem(), mergeConfig)
   104  			mergedPtr.Elem().Set(val)
   105  		}
   106  		return mergedPtr, true
   107  
   108  	case reflect.Slice:
   109  		if base.IsNil() && patch.IsNil() {
   110  			return reflect.Zero(commonType), false
   111  		}
   112  		if !patch.IsNil() {
   113  			// use patch
   114  			merged := reflect.MakeSlice(commonType, 0, patch.Len())
   115  			for i := 0; i < patch.Len(); i++ {
   116  				// recursively merge patch with itself. This will clone reference values.
   117  				val, _ := merge(patch.Index(i), patch.Index(i), mergeConfig)
   118  				merged = reflect.Append(merged, val)
   119  			}
   120  			return merged, true
   121  		}
   122  		// use base
   123  		merged := reflect.MakeSlice(commonType, 0, base.Len())
   124  		for i := 0; i < base.Len(); i++ {
   125  
   126  			// recursively merge base with itself. This will clone reference values.
   127  			val, _ := merge(base.Index(i), base.Index(i), mergeConfig)
   128  			merged = reflect.Append(merged, val)
   129  		}
   130  		return merged, true
   131  
   132  	case reflect.Map:
   133  		// maps are merged according to these rules:
   134  		// - if patch is not nil, replace the base map completely
   135  		// - otherwise, keep the base map
   136  		// - reference values (eg. slice/ptr/map) will be cloned
   137  		if base.IsNil() && patch.IsNil() {
   138  			return reflect.Zero(commonType), false
   139  		}
   140  		merged := reflect.MakeMap(commonType)
   141  		mapPtr := base
   142  		if !patch.IsNil() {
   143  			mapPtr = patch
   144  		}
   145  		for _, key := range mapPtr.MapKeys() {
   146  			// clone reference values
   147  			val, ok := merge(mapPtr.MapIndex(key), mapPtr.MapIndex(key), mergeConfig)
   148  			if !ok {
   149  				val = reflect.New(mapPtr.MapIndex(key).Type()).Elem()
   150  			}
   151  			merged.SetMapIndex(key, val)
   152  		}
   153  		return merged, true
   154  
   155  	case reflect.Interface:
   156  		var val reflect.Value
   157  		if base.IsNil() && patch.IsNil() {
   158  			return reflect.Zero(commonType), false
   159  		}
   160  
   161  		// clone reference values (if any)
   162  		if base.IsNil() {
   163  			val, _ = merge(patch.Elem(), patch.Elem(), mergeConfig)
   164  		} else if patch.IsNil() {
   165  			val, _ = merge(base.Elem(), base.Elem(), mergeConfig)
   166  		} else {
   167  			val, _ = merge(base.Elem(), patch.Elem(), mergeConfig)
   168  		}
   169  		return val, true
   170  
   171  	default:
   172  		return patch, true
   173  	}
   174  }