@@ -26,6 +26,35 @@ import (
2626// Adapted from https://www.jvt.me/posts/2024/01/09/go-json-nullable/
2727type NullableAttr [T any ] map [bool ]T
2828
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+
2958// NewNullableAttrWithValue is a convenience helper to allow constructing a
3059// NullableAttr with a given value, for instance to construct a field inside a
3160// struct without introducing an intermediate variable.
@@ -87,3 +116,65 @@ func (t NullableAttr[T]) IsSpecified() bool {
87116func (t * NullableAttr [T ]) SetUnspecified () {
88117 * t = map [bool ]T {}
89118}
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