-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathcast_assoc.go
104 lines (82 loc) · 2.6 KB
/
cast_assoc.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
package changeset
import (
"reflect"
"strconv"
"strings"
"github.com/go-rel/changeset/params"
)
// CastAssocErrorMessage is the default error message for CastAssoc when its invalid.
var CastAssocErrorMessage = "{field} is invalid"
// CastAssocRequiredMessage is the default error message for CastAssoc when its missing.
var CastAssocRequiredMessage = "{field} is required"
// ChangeFunc is changeset function.
type ChangeFunc func(interface{}, params.Params) *Changeset
// CastAssoc casts association changes using changeset function.
// Repo insert or update won't persist any changes generated by CastAssoc.
func CastAssoc(ch *Changeset, field string, fn ChangeFunc, opts ...Option) {
options := Options{
message: CastAssocErrorMessage,
}
options.apply(opts)
sourceField := options.sourceField
if sourceField == "" {
sourceField = field
}
typ, texist := ch.types[field]
valid := true
if texist && ch.params.Exists(sourceField) {
if typ.Kind() == reflect.Struct {
valid = castOne(ch, sourceField, field, fn)
} else if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Struct {
valid = castMany(ch, sourceField, field, fn)
}
}
if !valid {
msg := strings.Replace(options.message, "{field}", field, 1)
AddError(ch, field, msg)
}
_, found := ch.changes[field]
if options.required && !found {
options.message = CastAssocRequiredMessage
msg := strings.Replace(options.message, "{field}", field, 1)
AddError(ch, field, msg)
}
}
func castOne(ch *Changeset, fieldSource string, fieldTarget string, fn ChangeFunc) bool {
par, valid := ch.params.GetParams(fieldSource)
if !valid {
return false
}
var innerch *Changeset
if val, exist := ch.values[fieldTarget]; exist && val != nil {
innerch = fn(val, par)
} else {
innerch = fn(reflect.Zero(ch.types[fieldTarget]).Interface(), par)
}
ch.changes[fieldTarget] = innerch
// add errors to main errors
mergeErrors(ch, innerch, fieldTarget+".")
return true
}
func castMany(ch *Changeset, fieldSource string, fieldTarget string, fn ChangeFunc) bool {
spar, valid := ch.params.GetParamsSlice(fieldSource)
if !valid {
return false
}
data := reflect.Zero(ch.types[fieldTarget].Elem()).Interface()
chs := make([]*Changeset, len(spar))
for i, par := range spar {
innerch := fn(data, par)
chs[i] = innerch
// add errors to main errors
mergeErrors(ch, innerch, fieldTarget+"["+strconv.Itoa(i)+"].")
}
ch.changes[fieldTarget] = chs
return true
}
func mergeErrors(parent *Changeset, child *Changeset, prefix string) {
for _, err := range child.errors {
e := err.(Error)
AddError(parent, prefix+e.Field, e.Message)
}
}