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  }