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