-
Notifications
You must be signed in to change notification settings - Fork 1
/
coalescer.go
118 lines (112 loc) · 3.88 KB
/
coalescer.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
111
112
113
114
115
116
117
118
// Copyright 2022 Alexandre Dutra
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package goalesce
import "reflect"
// coalescer is the engine for merging and copying objets. It has two methods that satisfy
// DeepMergeFunc and DeepCopyFunc: deepMerge and deepCopy respectively.
type coalescer struct {
deepCopy DeepCopyFunc
deepMerge DeepMergeFunc
typeCopiers map[reflect.Type]DeepCopyFunc
typeMergers map[reflect.Type]DeepMergeFunc
sliceMerger DeepMergeFunc
sliceMergers map[ /* slice type */ reflect.Type]DeepMergeFunc
arrayMerger DeepMergeFunc
arrayMergers map[ /* slice type */ reflect.Type]DeepMergeFunc
fieldMergers map[ /* struct type */ reflect.Type]map[ /* field name */ string]DeepMergeFunc
zeroEmptySlice bool
errorOnCycle bool
seen map[uintptr]bool
}
func newCoalescer(opts ...Option) *coalescer {
c := &coalescer{
typeCopiers: make(map[reflect.Type]DeepCopyFunc),
typeMergers: make(map[reflect.Type]DeepMergeFunc),
sliceMergers: make(map[reflect.Type]DeepMergeFunc),
arrayMergers: make(map[reflect.Type]DeepMergeFunc),
fieldMergers: make(map[reflect.Type]map[string]DeepMergeFunc),
seen: make(map[uintptr]bool),
}
c.deepCopy = c.defaultDeepCopy
c.deepMerge = c.defaultDeepMerge
for _, opt := range opts {
opt(c)
}
return c
}
// defaultDeepMerge is the default implementation of DeepMergeFunc. It is used when the coalescer is
// created with default options. In the absence of a specific type merger, it merely delegates to
// the appropriate specialized merge methods, depending on the type of the values to merge.
func (c *coalescer) defaultDeepMerge(v1, v2 reflect.Value) (reflect.Value, error) {
if !v1.IsValid() {
return c.deepCopy(v2)
} else if !v2.IsValid() {
return c.deepCopy(v1)
}
if err := checkTypesMatch(v1.Type(), v2.Type()); err != nil {
return reflect.Value{}, err
}
if merger, found := c.typeMergers[v1.Type()]; found {
merged, err := merger(v1, v2)
if done, merged, err := checkCustomResult(merged, err, v1.Type()); done {
return merged, err
}
}
switch v1.Type().Kind() {
case reflect.Interface:
return c.deepMergeInterface(v1, v2)
case reflect.Ptr:
return c.deepMergePointer(v1, v2)
case reflect.Map:
return c.deepMergeMap(v1, v2)
case reflect.Struct:
return c.deepMergeStruct(v1, v2)
case reflect.Slice:
return c.deepMergeSlice(v1, v2)
case reflect.Array:
return c.deepMergeArray(v1, v2)
default:
return c.deepMergeAtomic(v1, v2)
}
}
// defaultDeepCopy is the default implementation of DeepCopyFunc. It is used when the coalescer is
// created with default options. In the absence of a specific type copier, it merely delegates to
// the appropriate specialized copy methods, depending on the type of the values to copy.
func (c *coalescer) defaultDeepCopy(v reflect.Value) (reflect.Value, error) {
if !v.IsValid() {
return v, nil
}
if copier, found := c.typeCopiers[v.Type()]; found {
copied, err := copier(v)
if done, copied, err := checkCustomResult(copied, err, v.Type()); done {
return copied, err
}
}
switch v.Type().Kind() {
case reflect.Interface:
return c.deepCopyInterface(v)
case reflect.Ptr:
return c.deepCopyPointer(v)
case reflect.Map:
return c.deepCopyMap(v)
case reflect.Struct:
return c.deepCopyStruct(v)
case reflect.Slice:
return c.deepCopySlice(v)
case reflect.Array:
return c.deepCopyArray(v)
default:
return c.deepCopyAtomic(v)
}
}