github.com/fretkak/mattermost-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 }