Skip to content

Commit 9dc1772

Browse files
authored
feat: add an option to hide empty sections (#3029)
1 parent 598e02e commit 9dc1772

File tree

5 files changed

+132
-11
lines changed

5 files changed

+132
-11
lines changed

internal/core/view.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ type ViewField struct {
2121
}
2222

2323
type ViewSection struct {
24-
Title string
25-
FieldName string
24+
Title string
25+
FieldName string
26+
HideIfEmpty bool
2627
}
2728

2829
func (v *View) getHumanMarshalerOpt() *human.MarshalOpt {
@@ -38,8 +39,9 @@ func (v *View) getHumanMarshalerOpt() *human.MarshalOpt {
3839
}
3940
for _, section := range v.Sections {
4041
opt.Sections = append(opt.Sections, &human.MarshalSection{
41-
Title: section.Title,
42-
FieldName: section.FieldName,
42+
Title: section.Title,
43+
FieldName: section.FieldName,
44+
HideIfEmpty: section.HideIfEmpty,
4345
})
4446
}
4547
opt.Title = v.Title

internal/gofields/gofields.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ import (
77
"strings"
88
)
99

10+
type NilValueError struct {
11+
Path string
12+
}
13+
14+
func NewNilValueError(path string) *NilValueError {
15+
return &NilValueError{Path: path}
16+
}
17+
18+
func (e *NilValueError) Error() string {
19+
return fmt.Sprintf("field %s is nil", e.Path)
20+
}
21+
1022
// GetValue will extract the value at the given path from the data Go struct
1123
// E.g data = { Friends: []Friend{ { Name: "John" } }, path = "Friends.0.Name" will return "John"
1224
func GetValue(data interface{}, path string) (interface{}, error) {
@@ -23,8 +35,8 @@ func getValue(value reflect.Value, parents []string, path []string) (reflect.Val
2335
return value, nil
2436
}
2537

26-
if !value.IsValid() || isNil(value) {
27-
return reflect.Value{}, fmt.Errorf("field %s is nil", strings.Join(parents, "."))
38+
if !value.IsValid() || IsNil(value) {
39+
return reflect.Value{}, NewNilValueError(strings.Join(parents, "."))
2840
}
2941

3042
if value.Type().Kind() == reflect.Ptr {
@@ -148,8 +160,8 @@ func listFields(t reflect.Type, parents []string, filter ListFieldFilter) []stri
148160
}
149161
}
150162

151-
// isNil test if a given value is nil. It is saf to call the mthod with non nillable value like scalar types
152-
func isNil(value reflect.Value) bool {
163+
// IsNil test if a given value is nil. It is saf to call the mthod with non nillable value like scalar types
164+
func IsNil(value reflect.Value) bool {
153165
return (value.Kind() == reflect.Ptr || value.Kind() == reflect.Slice || value.Kind() == reflect.Map) && value.IsNil()
154166
}
155167

internal/human/marshal.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,12 @@ func marshalStruct(value reflect.Value, opt *MarshalOpt) (string, error) {
102102
if err != nil {
103103
return "", err
104104
}
105-
sectionsStrs = append(sectionsStrs, sectionStr)
105+
106106
sectionFieldNames[section.FieldName] = true
107+
108+
if sectionStr != "" {
109+
sectionsStrs = append(sectionsStrs, sectionStr)
110+
}
107111
}
108112

109113
var marshal func(reflect.Value, []string) ([][]string, error)
@@ -379,8 +383,19 @@ func marshalSection(section *MarshalSection, value reflect.Value, opt *MarshalOp
379383

380384
field, err := gofields.GetValue(value.Interface(), section.FieldName)
381385
if err != nil {
386+
if section.HideIfEmpty {
387+
if _, ok := err.(*gofields.NilValueError); ok {
388+
return "", nil
389+
}
390+
}
391+
382392
return "", err
383393
}
394+
395+
if section.HideIfEmpty && reflect.ValueOf(field).IsZero() {
396+
return "", nil
397+
}
398+
384399
return Marshal(field, &subOpt)
385400
}
386401

internal/human/marshal_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,97 @@ func TestMarshal(t *testing.T) {
175175
`,
176176
}))
177177

178+
t.Run("hide if empty pointer 1", run(&testCase{
179+
data: &Human{
180+
Name: "Sherlock Holmes",
181+
Age: 42,
182+
Address: nil,
183+
Acquaintances: []*Acquaintance{
184+
{Name: "Dr watson", Link: "Assistant"},
185+
{Name: "Mrs. Hudson", Link: "Landlady"},
186+
},
187+
},
188+
opt: &MarshalOpt{
189+
Title: "Personal Information",
190+
Sections: []*MarshalSection{
191+
{FieldName: "Address", HideIfEmpty: true},
192+
{Title: "Relationship", FieldName: "Acquaintances"},
193+
},
194+
},
195+
result: `
196+
Personal Information:
197+
Name Sherlock Holmes
198+
Age 42
199+
200+
Relationship:
201+
NAME LINK
202+
Dr watson Assistant
203+
Mrs. Hudson Landlady
204+
`,
205+
}))
206+
207+
t.Run("hide if empty pointer 2", run(&testCase{
208+
data: &Human{
209+
Name: "Sherlock Holmes",
210+
Age: 42,
211+
Address: nil,
212+
Acquaintances: []*Acquaintance{
213+
{Name: "Dr watson", Link: "Assistant"},
214+
{Name: "Mrs. Hudson", Link: "Landlady"},
215+
},
216+
},
217+
opt: &MarshalOpt{
218+
Title: "Personal Information",
219+
Sections: []*MarshalSection{
220+
{FieldName: "Address.Street", HideIfEmpty: true},
221+
{Title: "Relationship", FieldName: "Acquaintances"},
222+
},
223+
},
224+
result: `
225+
Personal Information:
226+
Name Sherlock Holmes
227+
Age 42
228+
229+
Relationship:
230+
NAME LINK
231+
Dr watson Assistant
232+
Mrs. Hudson Landlady
233+
`,
234+
}))
235+
236+
t.Run("hide if empty string", run(&testCase{
237+
data: &Human{
238+
Name: "",
239+
Age: 42,
240+
Address: &Address{Street: "221b Baker St", City: "London"},
241+
Acquaintances: []*Acquaintance{
242+
{Name: "Dr watson", Link: "Assistant"},
243+
{Name: "Mrs. Hudson", Link: "Landlady"},
244+
},
245+
},
246+
opt: &MarshalOpt{
247+
Title: "Personal Information",
248+
Sections: []*MarshalSection{
249+
{FieldName: "Name", HideIfEmpty: true},
250+
{FieldName: "Address"},
251+
{Title: "Relationship", FieldName: "Acquaintances"},
252+
},
253+
},
254+
result: `
255+
Personal Information:
256+
Age 42
257+
258+
Address:
259+
Street 221b Baker St
260+
City London
261+
262+
Relationship:
263+
NAME LINK
264+
Dr watson Assistant
265+
Mrs. Hudson Landlady
266+
`,
267+
}))
268+
178269
t.Run("empty string", run(&testCase{
179270
data: "",
180271
result: `-`,

internal/human/specs.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ type MarshalFieldOpt struct {
2727
// MarshalSection describes a section to build from a given struct.
2828
// When marshalling, this section is shown under the main struct section.
2929
type MarshalSection struct {
30-
FieldName string
31-
Title string
30+
FieldName string
31+
Title string
32+
HideIfEmpty bool
3233
}
3334

3435
func (s *MarshalFieldOpt) getLabel() string {

0 commit comments

Comments
 (0)