@@ -296,22 +296,23 @@ func renderTable(out any, sort string, headers table.Row, filterColumns []string
296296// returned. If the table tag is malformed, an error is returned.
297297//
298298// The returned name is transformed from "snake_case" to "normal text".
299- func parseTableStructTag (field reflect.StructField ) (name string , defaultSort , noSortOpt , recursive , skipParentName bool , err error ) {
299+ func parseTableStructTag (field reflect.StructField ) (name string , defaultSort , noSortOpt , recursive , skipParentName , emptyNil bool , err error ) {
300300 tags , err := structtag .Parse (string (field .Tag ))
301301 if err != nil {
302- return "" , false , false , false , false , xerrors .Errorf ("parse struct field tag %q: %w" , string (field .Tag ), err )
302+ return "" , false , false , false , false , false , xerrors .Errorf ("parse struct field tag %q: %w" , string (field .Tag ), err )
303303 }
304304
305305 tag , err := tags .Get ("table" )
306306 if err != nil || tag .Name == "-" {
307307 // tags.Get only returns an error if the tag is not found.
308- return "" , false , false , false , false , nil
308+ return "" , false , false , false , false , false , nil
309309 }
310310
311311 defaultSortOpt := false
312312 noSortOpt = false
313313 recursiveOpt := false
314314 skipParentNameOpt := false
315+ emptyNilOpt := false
315316 for _ , opt := range tag .Options {
316317 switch opt {
317318 case "default_sort" :
@@ -326,12 +327,14 @@ func parseTableStructTag(field reflect.StructField) (name string, defaultSort, n
326327 // make sure the child name is unique across all nested structs in the parent.
327328 recursiveOpt = true
328329 skipParentNameOpt = true
330+ case "empty_nil" :
331+ emptyNilOpt = true
329332 default :
330- return "" , false , false , false , false , xerrors .Errorf ("unknown option %q in struct field tag" , opt )
333+ return "" , false , false , false , false , false , xerrors .Errorf ("unknown option %q in struct field tag" , opt )
331334 }
332335 }
333336
334- return strings .ReplaceAll (tag .Name , "_" , " " ), defaultSortOpt , noSortOpt , recursiveOpt , skipParentNameOpt , nil
337+ return strings .ReplaceAll (tag .Name , "_" , " " ), defaultSortOpt , noSortOpt , recursiveOpt , skipParentNameOpt , emptyNilOpt , nil
335338}
336339
337340func isStructOrStructPointer (t reflect.Type ) bool {
@@ -358,7 +361,7 @@ func typeToTableHeaders(t reflect.Type, requireDefault bool) ([]string, string,
358361 noSortOpt := false
359362 for i := 0 ; i < t .NumField (); i ++ {
360363 field := t .Field (i )
361- name , defaultSort , noSort , recursive , skip , err := parseTableStructTag (field )
364+ name , defaultSort , noSort , recursive , skip , _ , err := parseTableStructTag (field )
362365 if err != nil {
363366 return nil , "" , xerrors .Errorf ("parse struct tags for field %q in type %q: %w" , field .Name , t .String (), err )
364367 }
@@ -435,16 +438,22 @@ func valueToTableMap(val reflect.Value) (map[string]any, error) {
435438 for i := 0 ; i < val .NumField (); i ++ {
436439 field := val .Type ().Field (i )
437440 fieldVal := val .Field (i )
438- name , _ , _ , recursive , skip , err := parseTableStructTag (field )
441+ name , _ , _ , recursive , skip , emptyNil , err := parseTableStructTag (field )
439442 if err != nil {
440443 return nil , xerrors .Errorf ("parse struct tags for field %q in type %T: %w" , field .Name , val , err )
441444 }
442445 if name == "" {
443446 continue
444447 }
445448
446- // Recurse if it's a struct.
447449 fieldType := field .Type
450+
451+ // If empty_nil is set and this is a nil pointer, use a zero value.
452+ if emptyNil && fieldVal .Kind () == reflect .Pointer && fieldVal .IsNil () {
453+ fieldVal = reflect .New (fieldType .Elem ())
454+ }
455+
456+ // Recurse if it's a struct.
448457 if recursive {
449458 if ! isStructOrStructPointer (fieldType ) {
450459 return nil , xerrors .Errorf ("field %q in type %q is marked as recursive but does not contain a struct or a pointer to a struct" , field .Name , fieldType .String ())
@@ -467,7 +476,7 @@ func valueToTableMap(val reflect.Value) (map[string]any, error) {
467476 }
468477
469478 // Otherwise, we just use the field value.
470- row [name ] = val . Field ( i ) .Interface ()
479+ row [name ] = fieldVal .Interface ()
471480 }
472481
473482 return row , nil
0 commit comments