github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/toolkit/maputils/maputils.go (about)

     1  package maputils
     2  
     3  import (
     4  	"github.com/unionj-cloud/go-doudou/toolkit/sliceutils"
     5  	"reflect"
     6  )
     7  
     8  type ChangeType int
     9  
    10  const (
    11  	ADDED ChangeType = iota
    12  	MODIFIED
    13  	DELETED
    14  )
    15  
    16  type Change struct {
    17  	OldValue   interface{}
    18  	NewValue   interface{}
    19  	ChangeType ChangeType
    20  }
    21  
    22  func Diff(new, old map[string]interface{}) map[string]Change {
    23  	mp := map[string]bool{}
    24  	for k, _ := range old {
    25  		mp[k] = true
    26  	}
    27  	changes := make(map[string]Change)
    28  
    29  	if new != nil {
    30  		for key, value := range new {
    31  			//key state insert or update
    32  			//insert
    33  			if !mp[key] {
    34  				changes[key] = createAddChange(value)
    35  			} else {
    36  				//update
    37  				oldValue := old[key]
    38  				if !reflect.DeepEqual(oldValue, value) {
    39  					changes[key] = createModifyChange(oldValue, value)
    40  				}
    41  			}
    42  			delete(mp, key)
    43  		}
    44  	}
    45  
    46  	// remove del keys
    47  	for key := range mp {
    48  		//get old value and del
    49  		oldValue := old[key]
    50  		changes[key] = createDeletedChange(oldValue)
    51  	}
    52  
    53  	return changes
    54  }
    55  
    56  func createModifyChange(oldValue interface{}, newValue interface{}) Change {
    57  	return Change{
    58  		OldValue:   oldValue,
    59  		NewValue:   newValue,
    60  		ChangeType: MODIFIED,
    61  	}
    62  }
    63  
    64  func createAddChange(newValue interface{}) Change {
    65  	return Change{
    66  		NewValue:   newValue,
    67  		ChangeType: ADDED,
    68  	}
    69  }
    70  
    71  func createDeletedChange(oldValue interface{}) Change {
    72  	return Change{
    73  		OldValue:   oldValue,
    74  		ChangeType: DELETED,
    75  	}
    76  }
    77  
    78  var (
    79  	MaxDepth = 32
    80  )
    81  
    82  // Merge recursively merges the src and dst maps. Key conflicts are resolved by
    83  // preferring src, or recursively descending, if both src and dst are maps.
    84  // borrow code from https://github.com/peterbourgon/mergemap
    85  func Merge(dst, src map[string]interface{}) map[string]interface{} {
    86  	return merge(dst, src, 0, false)
    87  }
    88  
    89  func MergeOverwriteSlice(dst, src map[string]interface{}) map[string]interface{} {
    90  	return merge(dst, src, 0, true)
    91  }
    92  
    93  // overwrite means overwrite slice value
    94  func merge(dst, src map[string]interface{}, depth int, overwrite bool) map[string]interface{} {
    95  	if depth > MaxDepth {
    96  		panic("too deep!")
    97  	}
    98  	for key, srcVal := range src {
    99  		if dstVal, ok := dst[key]; ok {
   100  			srcMap, srcMapOk := mapify(srcVal)
   101  			dstMap, dstMapOk := mapify(dstVal)
   102  			if srcMapOk && dstMapOk {
   103  				srcVal = merge(dstMap, srcMap, depth+1, overwrite)
   104  				goto REWRITE
   105  			}
   106  			if overwrite {
   107  				goto REWRITE
   108  			}
   109  			srcSlice, srcSliceOk := sliceutils.TakeSliceArg(srcVal)
   110  			dstSlice, dstSliceOk := sliceutils.TakeSliceArg(dstVal)
   111  			if srcSliceOk && dstSliceOk {
   112  				merged := make([]interface{}, 0)
   113  				kv := make(map[interface{}]struct{})
   114  				for _, item := range dstSlice {
   115  					if !reflect.ValueOf(item).Type().Comparable() {
   116  						merged = append(merged, item)
   117  						continue
   118  					}
   119  					if _, exists := kv[item]; !exists {
   120  						merged = append(merged, item)
   121  						kv[item] = struct{}{}
   122  					}
   123  				}
   124  				for _, item := range srcSlice {
   125  					if !reflect.ValueOf(item).Type().Comparable() {
   126  						merged = append(merged, item)
   127  						continue
   128  					}
   129  					if _, exists := kv[item]; !exists {
   130  						merged = append(merged, item)
   131  						kv[item] = struct{}{}
   132  					}
   133  				}
   134  				srcVal = merged
   135  			}
   136  		}
   137  	REWRITE:
   138  		dst[key] = srcVal
   139  	}
   140  	return dst
   141  }
   142  
   143  func mapify(i interface{}) (map[string]interface{}, bool) {
   144  	value := reflect.ValueOf(i)
   145  	if value.Kind() == reflect.Map {
   146  		m := map[string]interface{}{}
   147  		for _, k := range value.MapKeys() {
   148  			m[k.String()] = value.MapIndex(k).Interface()
   149  		}
   150  		return m, true
   151  	}
   152  	return map[string]interface{}{}, false
   153  }