Skip to content

Commit f54ab10

Browse files
authored
Improved omitempty tag and added support for omitnil
2 parents 7c6df26 + 7e02f7b commit f54ab10

2 files changed

Lines changed: 100 additions & 7 deletions

File tree

struct.go

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package goqux
22

33
import (
4+
"fmt"
45
"reflect"
56
"strings"
67
"time"
@@ -26,7 +27,9 @@ const (
2627
// Same as default now but will inject time.Now().UTC()
2728
defaultNowUtc = "now_utc"
2829
// omitempty will skip the field if it is zero value
29-
omitEmpty = ",omitempty"
30+
omitEmpty = "omitempty"
31+
// omitnil will skip the field if it is nil
32+
omitNil = "omitnil"
3033
)
3134

3235
func convertMapToSQLValuer(m map[string]any) map[string]SQLValuer {
@@ -58,10 +61,16 @@ func encodeValues(v any, skipType string, skipZeroValues bool) map[string]SQLVal
5861
columnName := strcase.ToSnake(f.Name)
5962
if dbTag := f.Tag.Get(tagNameDb); dbTag != "" {
6063
if strings.Contains(dbTag, omitEmpty) {
61-
if value.IsZero() {
64+
if !value.IsValid() || value.IsZero() { // IsZero panic on valid values
6265
continue
6366
}
64-
dbTag = cleanDbTag(dbTag)
67+
dbTag = cleanDbTag(dbTag, omitEmpty)
68+
}
69+
if strings.Contains(dbTag, omitNil) {
70+
if value.IsNil() {
71+
continue
72+
}
73+
dbTag = cleanDbTag(dbTag, omitNil)
6574
}
6675
columnName = dbTag
6776
}
@@ -91,7 +100,7 @@ func getColumnsFromStruct(table exp.IdentifierExpression, s any, skipType string
91100
}
92101
var colName string
93102
if dbTag := f.Tag.Get(tagNameDb); dbTag != "" {
94-
colName = cleanDbTag(dbTag)
103+
colName = cleanDbTag(dbTag, omitEmpty, omitNil)
95104
} else {
96105
colName = strcase.ToSnake(f.Name)
97106
}
@@ -100,11 +109,32 @@ func getColumnsFromStruct(table exp.IdentifierExpression, s any, skipType string
100109
return cols
101110
}
102111

103-
func cleanDbTag(tag string) string {
104-
if strings.Contains(tag, omitEmpty) {
105-
tag = strings.ReplaceAll(tag, omitEmpty, "")
112+
func cleanDbTag(tag string, tagsToClean ...string) string {
113+
for _, tagToClean := range tagsToClean {
114+
// Handle case where tag is just the partToClean
115+
if tag == tagToClean {
116+
return ""
117+
}
118+
119+
// Handle case where tag starts with partToClean
120+
if strings.HasPrefix(tag, fmt.Sprintf("%s,", tagToClean)) {
121+
tag = strings.TrimPrefix(tag, fmt.Sprintf("%s,", tagToClean))
122+
}
123+
124+
// Handle case where tag ends with partToClean
125+
if strings.HasSuffix(tag, fmt.Sprintf(",%s", tagToClean)) {
126+
tag = strings.TrimSuffix(tag, fmt.Sprintf(",%s", tagToClean))
127+
}
128+
129+
// Handle case where tagToClean tag is in the middle
130+
if strings.Contains(tag, fmt.Sprintf(",%s,", tagToClean)) {
131+
tag = strings.ReplaceAll(tag, fmt.Sprintf(",%s,", tagToClean), ",")
132+
}
106133
}
107134

135+
// Clean up any remaining commas
136+
tag = strings.Trim(tag, ",")
137+
108138
return tag
109139
}
110140

struct_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,69 @@ func TestEncodeValues(t *testing.T) {
101101
skipFlag: skipInsert,
102102
skipZeroValues: false,
103103
},
104+
{
105+
name: "encode_omitnil_nil",
106+
model: struct {
107+
PtrField *int `db:"ptr_field,omitnil"`
108+
}{PtrField: nil},
109+
values: map[string]SQLValuer{},
110+
skipFlag: skipInsert,
111+
skipZeroValues: false,
112+
},
113+
{
114+
name: "encode_omitnil_non_nil",
115+
model: struct {
116+
PtrField *int `db:"ptr_field,omitnil"`
117+
}{PtrField: new(int)},
118+
values: map[string]SQLValuer{"ptr_field": {new(int)}},
119+
skipFlag: skipInsert,
120+
skipZeroValues: false,
121+
},
122+
{
123+
name: "encode_omitnil_and_omitempty",
124+
model: struct {
125+
PtrField *int `db:"ptr_field,omitnil,omitempty"`
126+
}{PtrField: new(int)},
127+
values: map[string]SQLValuer{"ptr_field": {new(int)}},
128+
skipFlag: skipInsert,
129+
skipZeroValues: false,
130+
},
131+
{
132+
name: "encode_only_omitempty",
133+
model: struct {
134+
Field int `db:"omitempty"`
135+
}{Field: 0},
136+
values: map[string]SQLValuer{},
137+
skipFlag: skipInsert,
138+
skipZeroValues: false,
139+
},
140+
{
141+
name: "encode_only_omitnil",
142+
model: struct {
143+
Field *int `db:"omitnil"`
144+
}{Field: nil},
145+
values: map[string]SQLValuer{},
146+
skipFlag: skipInsert,
147+
skipZeroValues: false,
148+
},
149+
{
150+
name: "encode_omitnil_prefix",
151+
model: struct {
152+
Field *int `db:"omitnil,field_name"`
153+
}{Field: nil},
154+
values: map[string]SQLValuer{},
155+
skipFlag: skipInsert,
156+
skipZeroValues: false,
157+
},
158+
{
159+
name: "encode_omitempty_suffix",
160+
model: struct {
161+
Field int `db:"field_name,omitempty"`
162+
}{Field: 0},
163+
values: map[string]SQLValuer{},
164+
skipFlag: skipInsert,
165+
skipZeroValues: false,
166+
},
104167
}
105168
for _, tt := range tableTests {
106169
t.Run(tt.name, func(t *testing.T) {

0 commit comments

Comments
 (0)