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  }