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  }