github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/utils/merge.go (about) 1 /* 2 Licensed under the Apache License, Version 2.0 (the "License"); 3 you may not use this file except in compliance with the License. 4 You may obtain a copy of the License at 5 6 http://www.apache.org/licenses/LICENSE-2.0 7 8 Unless required by applicable law or agreed to in writing, software 9 distributed under the License is distributed on an "AS IS" BASIS, 10 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 See the License for the specific language governing permissions and 12 limitations under the License. 13 */ 14 15 /* 16 The code to recursively merge JSON-like data structures (maps, slices, values) 17 Based on go-merge library (https://github.com/divideandconquer/go-merge) with some major changes 18 */ 19 20 package utils 21 22 import ( 23 "reflect" 24 ) 25 26 // Merge will take two data sets and merge them together - returning a new data set 27 func Merge(base, override interface{}) interface{} { 28 //reflect and recurse 29 b := reflect.ValueOf(base) 30 o := reflect.ValueOf(override) 31 ret := mergeRecursive(b, o) 32 if !ret.IsValid() { 33 return nil 34 } 35 36 return ret.Interface() 37 } 38 39 func mergeRecursive(base, override reflect.Value) reflect.Value { 40 if !override.IsValid() || !base.IsValid() || base.Type() != override.Type() { 41 return override 42 } 43 var result reflect.Value 44 45 switch base.Kind() { 46 case reflect.Ptr: 47 switch base.Elem().Kind() { 48 case reflect.Ptr: 49 fallthrough 50 case reflect.Slice: 51 fallthrough 52 case reflect.Map: 53 // Pointers to complex types should recurse if they aren't nil 54 if base.IsNil() { 55 result = override 56 } else if override.IsNil() { 57 result = base 58 } else { 59 result = mergeRecursive(base.Elem(), override.Elem()) 60 61 } 62 default: 63 // Pointers to basic types should just override 64 result = override 65 } 66 67 case reflect.Interface: 68 // Interfaces should just be unwrapped and recursed through 69 result = mergeRecursive(base.Elem(), override.Elem()) 70 71 case reflect.Map: 72 73 // For Maps we copy the base data, and then replace it with merged data 74 // We use two for loops to make sure all map keys from base and all keys from 75 // override exist in the result just in case one of the maps is sparse. 76 elementsAreValues := base.Type().Elem().Kind() != reflect.Ptr 77 78 result = reflect.MakeMap(base.Type()) 79 // Copy from base first 80 for _, key := range base.MapKeys() { 81 result.SetMapIndex(key, base.MapIndex(key)) 82 } 83 84 // Override with values from override if they exist 85 if override.Kind() == reflect.Map { 86 for _, key := range override.MapKeys() { 87 overrideVal := override.MapIndex(key) 88 baseVal := base.MapIndex(key) 89 if !overrideVal.IsValid() { 90 continue 91 } 92 93 // if there is no base value, just set the override 94 if !baseVal.IsValid() { 95 result.SetMapIndex(key, overrideVal) 96 continue 97 } 98 99 // Merge the values and set in the result 100 newVal := mergeRecursive(baseVal, overrideVal) 101 if elementsAreValues && newVal.Kind() == reflect.Ptr { 102 result.SetMapIndex(key, newVal.Elem()) 103 104 } else { 105 result.SetMapIndex(key, newVal) 106 } 107 } 108 } 109 case reflect.Slice: 110 result = reflect.AppendSlice(base, override) 111 default: 112 result = override 113 } 114 return result 115 }