-
Notifications
You must be signed in to change notification settings - Fork 134
/
diff.go
110 lines (95 loc) · 2.96 KB
/
diff.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package fastly
import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
// KeyFunc calculates a key from an element
type KeyFunc func(interface{}) (interface{}, error)
// SetDiff diffs two sets using a key to identify which elements have been added, changed, removed or not modified.
//
// This object compares sets using Terraform's schema.Set methods (e.g. Difference() and Intersection())
// so that the same differences displayed to the user are honoured here.
//
// SetDiff however is able to tell if two elements from two distinct sets have the same key. This is useful to detect
// that an element should be updated instead of recreated on the remote server.
type SetDiff struct {
keyFunc KeyFunc
}
// DiffResult contains the differences between two sets
type DiffResult struct {
Added []interface{}
Modified []interface{}
Deleted []interface{}
Unmodified []interface{}
}
// NewSetDiff creates a new SetDiff with a provided KeyFunc.
func NewSetDiff(keyFunc KeyFunc) *SetDiff {
return &SetDiff{
keyFunc: keyFunc,
}
}
// Diff diffs two Set objects and returns a DiffResult object containing the diffs.
//
// The DiffResult object will contain the elements from newSet on the Modified field.
func (h *SetDiff) Diff(oldSet, newSet *schema.Set) (*DiffResult, error) {
// Convert the set into a map to facilitate lookup
oldSetMap := map[interface{}]interface{}{}
newSetMap := map[interface{}]interface{}{}
for _, elem := range oldSet.List() {
key, err := h.computeKey(elem)
if err != nil {
return nil, newElementKeyError(elem, err)
}
oldSetMap[key] = elem
}
for _, elem := range newSet.List() {
key, err := h.computeKey(elem)
if err != nil {
return nil, newElementKeyError(elem, err)
}
newSetMap[key] = elem
}
// Compute all added and modified elements using Terraform set comparison method.
var added, modified []interface{}
for _, newElem := range newSet.Difference(oldSet).List() {
key, err := h.computeKey(newElem)
if err != nil {
return nil, newElementKeyError(newElem, err)
}
if oldSetMap[key] != nil {
modified = append(modified, newElem)
} else {
added = append(added, newElem)
}
}
var deleted []interface{}
for _, oldElem := range oldSet.Difference(newSet).List() {
key, err := h.computeKey(oldElem)
if err != nil {
return nil, newElementKeyError(oldElem, err)
}
if newSetMap[key] == nil {
deleted = append(deleted, oldElem)
}
}
unmodified := oldSet.Intersection(newSet).List()
return &DiffResult{
Added: added,
Modified: modified,
Deleted: deleted,
Unmodified: unmodified,
}, nil
}
func (h *SetDiff) computeKey(elem interface{}) (interface{}, error) {
key, err := h.keyFunc(elem)
if err != nil {
return nil, err
}
if key == nil {
return nil, fmt.Errorf("invalid key for element %v, %v", elem, err)
}
return key, nil
}
func newElementKeyError(elem interface{}, err error) error {
return fmt.Errorf("error computing the key for element %v, %v", elem, err)
}