github.com/yanndegat/hiera@v0.6.8/merge/strategy.go (about) 1 package merge 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/lyraproj/dgo/dgo" 8 "github.com/lyraproj/dgo/vf" 9 "github.com/yanndegat/hiera/api" 10 ) 11 12 type ( 13 deepMerge struct{ opts dgo.Map } 14 15 hashMerge struct{} 16 17 firstFound struct{} 18 19 unique struct{} 20 ) 21 22 // GetStrategy returns the merge.MergeStrategy that corresponds to the given name. The 23 // options argument is only applicable to deep merge 24 func GetStrategy(n string, opts dgo.Map) api.MergeStrategy { 25 switch n { 26 case `first`: 27 return &firstFound{} 28 case `unique`: 29 return &unique{} 30 case `hash`: 31 return &hashMerge{} 32 case `deep`: 33 if opts == nil { 34 opts = vf.Map() 35 } 36 return &deepMerge{opts} 37 default: 38 panic(fmt.Errorf(`unknown merge strategy '%s'`, n)) 39 } 40 } 41 42 type merger interface { 43 api.MergeStrategy 44 45 merge(a, b dgo.Value) dgo.Value 46 47 mergeSingle(v reflect.Value, vf func(l interface{}) dgo.Value) dgo.Value 48 49 convertValue(v dgo.Value) dgo.Value 50 } 51 52 func doLookup(s merger, vs interface{}, ic api.Invocation, vf func(l interface{}) dgo.Value) dgo.Value { 53 vsr := reflect.ValueOf(vs) 54 if vsr.Kind() != reflect.Slice { 55 return nil 56 } 57 top := vsr.Len() 58 switch top { 59 case 0: 60 return nil 61 case 1: 62 return s.mergeSingle(vsr.Index(0), vf) 63 default: 64 return ic.WithMerge(s, func() dgo.Value { 65 var memo dgo.Value 66 for idx := 0; idx < top; idx++ { 67 v := variantLookup(vsr.Index(idx), vf) 68 if v != nil { 69 if memo == nil { 70 memo = s.convertValue(v) 71 } else { 72 memo = s.merge(memo, v) 73 } 74 } 75 } 76 if memo != nil { 77 ic.ReportMergeResult(memo) 78 } 79 return memo 80 }) 81 } 82 } 83 84 func variantLookup(v reflect.Value, vf func(l interface{}) dgo.Value) dgo.Value { 85 if v.CanInterface() { 86 return vf(v.Interface()) 87 } 88 return nil 89 } 90 91 func (d *firstFound) Name() string { 92 return `first` 93 } 94 95 func (d *firstFound) Label() string { 96 return `first found strategy` 97 } 98 99 func (d *firstFound) MergeLookup(vs interface{}, ic api.Invocation, f func(location interface{}) dgo.Value) dgo.Value { 100 vsr := reflect.ValueOf(vs) 101 if vsr.Kind() != reflect.Slice { 102 return nil 103 } 104 top := vsr.Len() 105 switch top { 106 case 0: 107 return nil 108 case 1: 109 return variantLookup(vsr.Index(0), f) 110 default: 111 var v dgo.Value 112 for idx := 0; idx < top; idx++ { 113 v = variantLookup(vsr.Index(idx), f) 114 if v != nil { 115 break 116 } 117 } 118 if v != nil { 119 ic.ReportMergeResult(v) 120 } 121 return v 122 } 123 } 124 125 func (d *firstFound) Options() dgo.Map { 126 return vf.Map() 127 } 128 129 func (d *firstFound) mergeSingle(v reflect.Value, vf func(l interface{}) dgo.Value) dgo.Value { 130 return variantLookup(v, vf) 131 } 132 133 func (d *firstFound) convertValue(v dgo.Value) dgo.Value { 134 return v 135 } 136 137 func (d *firstFound) merge(a, b dgo.Value) dgo.Value { 138 return a 139 } 140 141 func (d *unique) Name() string { 142 return `unique` 143 } 144 145 func (d *unique) Label() string { 146 return `unique merge strategy` 147 } 148 149 func (d *unique) MergeLookup(vs interface{}, ic api.Invocation, f func(location interface{}) dgo.Value) dgo.Value { 150 return doLookup(d, vs, ic, f) 151 } 152 153 func (d *unique) Options() dgo.Map { 154 return vf.Map() 155 } 156 157 func (d *unique) mergeSingle(rv reflect.Value, vf func(l interface{}) dgo.Value) dgo.Value { 158 v := variantLookup(rv, vf) 159 if av, ok := v.(dgo.Array); ok { 160 return av.Flatten().Unique() 161 } 162 return v 163 } 164 165 func (d *unique) convertValue(v dgo.Value) dgo.Value { 166 if av, ok := v.(dgo.Array); ok { 167 return av.Flatten() 168 } 169 return vf.Values(v) 170 } 171 172 func (d *unique) merge(a, b dgo.Value) dgo.Value { 173 return d.convertValue(a).(dgo.Array).WithAll(d.convertValue(b).(dgo.Array)).Unique() 174 } 175 176 func (d *deepMerge) Name() string { 177 return `deep` 178 } 179 180 func (d *deepMerge) Label() string { 181 return `deep merge strategy` 182 } 183 184 func (d *deepMerge) MergeLookup(vs interface{}, ic api.Invocation, f func(location interface{}) dgo.Value) dgo.Value { 185 return doLookup(d, vs, ic, f) 186 } 187 188 func (d *deepMerge) Options() dgo.Map { 189 return d.opts 190 } 191 192 func (d *deepMerge) mergeSingle(v reflect.Value, vf func(l interface{}) dgo.Value) dgo.Value { 193 return variantLookup(v, vf) 194 } 195 196 func (d *deepMerge) convertValue(v dgo.Value) dgo.Value { 197 return v 198 } 199 200 func (d *deepMerge) merge(a, b dgo.Value) dgo.Value { 201 v, _ := Deep(a, b, d.opts) 202 return v 203 } 204 205 func (d *hashMerge) Name() string { 206 return `hash` 207 } 208 209 func (d *hashMerge) Label() string { 210 return `hash merge strategy` 211 } 212 213 func (d *hashMerge) MergeLookup(vs interface{}, ic api.Invocation, f func(location interface{}) dgo.Value) dgo.Value { 214 return doLookup(d, vs, ic, f) 215 } 216 217 func (d *hashMerge) Options() dgo.Map { 218 return vf.Map() 219 } 220 221 func (d *hashMerge) mergeSingle(v reflect.Value, vf func(l interface{}) dgo.Value) dgo.Value { 222 return variantLookup(v, vf) 223 } 224 225 func (d *hashMerge) convertValue(v dgo.Value) dgo.Value { 226 return v 227 } 228 229 func (d *hashMerge) merge(a, b dgo.Value) dgo.Value { 230 if ah, ok := a.(dgo.Map); ok { 231 var bh dgo.Map 232 if bh, ok = b.(dgo.Map); ok { 233 return bh.Merge(ah) 234 } 235 } 236 return a 237 }