@@ -26,6 +26,35 @@ import (
26
26
// Adapted from https://www.jvt.me/posts/2024/01/09/go-json-nullable/
27
27
type NullableAttr [T any ] map [bool ]T
28
28
29
+ // NullableRelationship is a generic type, which implements a field that can be one of three states:
30
+ //
31
+ // - relationship is not set in the request
32
+ // - relationship is explicitly set to `null` in the request
33
+ // - relationship is explicitly set to a valid relationship value in the request
34
+ //
35
+ // NullableRelationship is intended to be used with JSON marshalling and unmarshalling.
36
+ // This is generally useful for PATCH requests, where relationships with zero
37
+ // values are intentionally not marshaled into the request payload so that
38
+ // existing attribute values are not overwritten.
39
+ //
40
+ // Internal implementation details:
41
+ //
42
+ // - map[true]T means a value was provided
43
+ // - map[false]T means an explicit null was provided
44
+ // - nil or zero map means the field was not provided
45
+ //
46
+ // If the relationship is expected to be optional, add the `omitempty` JSON tags. Do NOT use `*NullableRelationship`!
47
+ //
48
+ // Slice types are not currently supported for NullableRelationships as the nullable nature can be expressed via empty array
49
+ // `polyrelation` JSON tags are NOT currently supported.
50
+ //
51
+ // NullableRelationships must have an inner type of pointer:
52
+ //
53
+ // - NullableRelationship[*Comment] - valid
54
+ // - NullableRelationship[[]*Comment] - invalid
55
+ // - NullableRelationship[Comment] - invalid
56
+ type NullableRelationship [T any ] map [bool ]T
57
+
29
58
// NewNullableAttrWithValue is a convenience helper to allow constructing a
30
59
// NullableAttr with a given value, for instance to construct a field inside a
31
60
// struct without introducing an intermediate variable.
@@ -87,3 +116,65 @@ func (t NullableAttr[T]) IsSpecified() bool {
87
116
func (t * NullableAttr [T ]) SetUnspecified () {
88
117
* t = map [bool ]T {}
89
118
}
119
+
120
+ // NewNullableAttrWithValue is a convenience helper to allow constructing a
121
+ // NullableAttr with a given value, for instance to construct a field inside a
122
+ // struct without introducing an intermediate variable.
123
+ func NewNullableRelationshipWithValue [T any ](t T ) NullableRelationship [T ] {
124
+ var n NullableRelationship [T ]
125
+ n .Set (t )
126
+ return n
127
+ }
128
+
129
+ // NewNullNullableAttr is a convenience helper to allow constructing a NullableAttr with
130
+ // an explicit `null`, for instance to construct a field inside a struct
131
+ // without introducing an intermediate variable
132
+ func NewNullNullableRelationship [T any ]() NullableRelationship [T ] {
133
+ var n NullableRelationship [T ]
134
+ n .SetNull ()
135
+ return n
136
+ }
137
+
138
+ // Get retrieves the underlying value, if present, and returns an error if the value was not present
139
+ func (t NullableRelationship [T ]) Get () (T , error ) {
140
+ var empty T
141
+ if t .IsNull () {
142
+ return empty , errors .New ("value is null" )
143
+ }
144
+ if ! t .IsSpecified () {
145
+ return empty , errors .New ("value is not specified" )
146
+ }
147
+ return t [true ], nil
148
+ }
149
+
150
+ // Set sets the underlying value to a given value
151
+ func (t * NullableRelationship [T ]) Set (value T ) {
152
+ * t = map [bool ]T {true : value }
153
+ }
154
+
155
+ // Set sets the underlying value to a given value
156
+ func (t * NullableRelationship [T ]) SetInterface (value interface {}) {
157
+ t .Set (value .(T ))
158
+ }
159
+
160
+ // IsNull indicates whether the field was sent, and had a value of `null`
161
+ func (t NullableRelationship [T ]) IsNull () bool {
162
+ _ , foundNull := t [false ]
163
+ return foundNull
164
+ }
165
+
166
+ // SetNull sets the value to an explicit `null`
167
+ func (t * NullableRelationship [T ]) SetNull () {
168
+ var empty T
169
+ * t = map [bool ]T {false : empty }
170
+ }
171
+
172
+ // IsSpecified indicates whether the field was sent
173
+ func (t NullableRelationship [T ]) IsSpecified () bool {
174
+ return len (t ) != 0
175
+ }
176
+
177
+ // SetUnspecified sets the value to be absent from the serialized payload
178
+ func (t * NullableRelationship [T ]) SetUnspecified () {
179
+ * t = map [bool ]T {}
180
+ }
0 commit comments