hub.fastgit.org/hashicorp/consul.git@v1.4.5/agent/config/merge.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "reflect" 6 ) 7 8 // Merge recursively combines a set of config file structures into a single structure 9 // according to the following rules: 10 // 11 // * only values of type struct, slice, map and pointer to simple types are allowed. Other types panic. 12 // * when merging two structs the result is the recursive merge of all fields according to the rules below 13 // * when merging two slices the result is the second slice appended to the first 14 // * when merging two maps the result is the second map overlaid on the first 15 // * when merging two pointer values the result is the second value if it is not nil, otherwise the first 16 func Merge(files ...Config) Config { 17 var a Config 18 for _, b := range files { 19 a = merge(a, b).(Config) 20 } 21 return a 22 } 23 24 func merge(a, b interface{}) interface{} { 25 return mergeValue(reflect.ValueOf(a), reflect.ValueOf(b)).Interface() 26 } 27 28 func mergeValue(a, b reflect.Value) reflect.Value { 29 switch a.Kind() { 30 case reflect.Map: 31 r := reflect.MakeMap(a.Type()) 32 for _, k := range a.MapKeys() { 33 v := a.MapIndex(k) 34 r.SetMapIndex(k, v) 35 } 36 for _, k := range b.MapKeys() { 37 v := b.MapIndex(k) 38 r.SetMapIndex(k, v) 39 } 40 return r 41 42 case reflect.Ptr: 43 if !b.IsNil() { 44 return b 45 } 46 return a 47 48 case reflect.Slice: 49 if !a.IsValid() { 50 a = reflect.Zero(a.Type()) 51 } 52 return reflect.AppendSlice(a, b) 53 54 case reflect.Struct: 55 r := reflect.New(a.Type()) // &struct{} 56 for i := 0; i < a.NumField(); i++ { 57 v := mergeValue(a.Field(i), b.Field(i)) 58 r.Elem().Field(i).Set(v) 59 } 60 return r.Elem() // *struct 61 62 default: 63 panic(fmt.Sprintf("unsupported element type: %v", a.Type())) 64 } 65 }