forked from go-rel/rel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
association.go
150 lines (120 loc) · 3.32 KB
/
association.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package rel
import (
"reflect"
)
// Association provides abstraction to work with association of document or collection.
type Association struct {
meta AssociationMeta
rv reflect.Value
}
// Type of association.
func (a Association) Type() AssociationType {
return a.meta.Type()
}
// Document returns association target as document.
// If association is zero, second return value will be false.
func (a Association) Document() (*Document, bool) {
return a.document(false)
}
// LazyDocument is a lazy version of Document.
// If rv is a null pointer, it returns a document that delays setting the value of rv
// until Document#Add() is called.
func (a Association) LazyDocument() (*Document, bool) {
return a.document(true)
}
func (a Association) document(lazy bool) (*Document, bool) {
var (
rv = reflectValueFieldByIndex(a.rv, a.meta.targetIndex, !lazy)
)
switch rv.Kind() {
case reflect.Ptr:
if rv.IsNil() {
if !lazy {
rv.Set(reflect.New(rv.Type().Elem()))
}
return NewDocument(rv), false
}
var (
doc = NewDocument(rv)
)
return doc, doc.Persisted()
default:
var (
doc = NewDocument(rv.Addr())
)
return doc, doc.Persisted()
}
}
// Collection returns association target as collection.
// If association is zero, second return value will be false.
func (a Association) Collection() (*Collection, bool) {
var (
rv = reflectValueFieldByIndex(a.rv, a.meta.targetIndex, true)
loaded = !rv.IsNil()
)
if rv.Kind() == reflect.Ptr {
if !loaded {
rv.Set(reflect.New(rv.Type().Elem()))
rv.Elem().Set(reflect.MakeSlice(rv.Elem().Type(), 0, 0))
}
return NewCollection(rv), loaded
}
if !loaded {
rv.Set(reflect.MakeSlice(rv.Type(), 0, 0))
}
return NewCollection(rv.Addr()), loaded
}
// IsZero returns true if association is not loaded.
func (a Association) IsZero() bool {
var (
rv = reflectValueFieldByIndex(a.rv, a.meta.targetIndex, false)
)
return isDeepZero(reflect.Indirect(rv), 1)
}
// ReferenceField of the association.
func (a Association) ReferenceField() string {
return a.meta.ReferenceField()
}
// ReferenceValue of the association.
func (a Association) ReferenceValue() any {
return indirectInterface(reflectValueFieldByIndex(a.rv, a.meta.referenceIndex, false))
}
// ForeignField of the association.
func (a Association) ForeignField() string {
return a.meta.ForeignField()
}
// ForeignValue of the association.
// It'll panic if association type is has many.
func (a Association) ForeignValue() any {
if a.Type() == HasMany {
panic("rel: cannot infer foreign value for has many or many to many association")
}
var (
rv = reflectValueFieldByIndex(a.rv, a.meta.targetIndex, false)
)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
return indirectInterface(reflectValueFieldByIndex(rv, a.meta.foreignIndex, false))
}
// Through return intermediary association.
func (a Association) Through() string {
return a.meta.Through()
}
// Autoload assoc setting when parent is loaded.
func (a Association) Autoload() bool {
return a.meta.Autoload()
}
// Autosave setting when parent is created/updated/deleted.
func (a Association) Autosave() bool {
return a.meta.Autosave()
}
func newAssociation(rv reflect.Value, index []int) Association {
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
return Association{
meta: getAssociationMeta(rv.Type(), index),
rv: rv,
}
}