Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

omitempty also considers structs with all zero values "empty". #356

Merged
merged 1 commit into from Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions encode.go
Expand Up @@ -89,6 +89,17 @@ type Marshaler interface {
//
// Go maps will be sorted alphabetically by key for deterministic output.
//
// The toml struct tag can be used to provide the key name; if omitted the
// struct field name will be used. If the "omitempty" option is present the
// following value will be skipped:
//
// - arrays, slices, maps, and string with len of 0
// - struct with all zero values
// - bool false
//
// If omitzero is given all int and float types with a value of 0 will be
// skipped.
//
// Encoding Go values without a corresponding TOML representation will return an
// error. Examples of this includes maps with non-string keys, slices with nil
// elements, embedded non-struct types, and nested slices containing maps or
Expand Down Expand Up @@ -655,6 +666,8 @@ func isEmpty(rv reflect.Value) bool {
switch rv.Kind() {
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
return rv.Len() == 0
case reflect.Struct:
return reflect.Zero(rv.Type()).Interface() == rv.Interface()
case reflect.Bool:
return !rv.Bool()
}
Expand Down
67 changes: 67 additions & 0 deletions encode_test.go
Expand Up @@ -116,13 +116,78 @@ func TestEncodeArrayHashWithNormalHashOrder(t *testing.T) {
encodeExpected(t, "array hash with normal hash order", val, expected, nil)
}

func TestEncodeOmitEmptyStruct(t *testing.T) {
type (
T struct{ Int int }
Tpriv struct {
Int int
private int
}
Ttime struct {
Time time.Time
}
)

tests := []struct {
in interface{}
want string
}{
{struct {
F T `toml:"f,omitempty"`
}{}, ""},
{struct {
F T `toml:"f,omitempty"`
}{T{1}}, "[f]\n Int = 1"},

{struct {
F Tpriv `toml:"f,omitempty"`
}{}, ""},
{struct {
F Tpriv `toml:"f,omitempty"`
}{Tpriv{1, 0}}, "[f]\n Int = 1"},

// Private field being set also counts as "not empty".
{struct {
F Tpriv `toml:"f,omitempty"`
}{Tpriv{0, 1}}, "[f]\n Int = 0"},

// time.Time is common use case, so test that explicitly.
{struct {
F Ttime `toml:"t,omitempty"`
}{}, ""},
{struct {
F Ttime `toml:"t,omitempty"`
}{Ttime{time.Time{}.Add(1)}}, "[t]\n Time = 0001-01-01T00:00:00.000000001Z"},

// TODO: also test with MarshalText, MarshalTOML returning non-zero
// value.
}

for _, tt := range tests {
t.Run("", func(t *testing.T) {
buf := new(bytes.Buffer)

err := NewEncoder(buf).Encode(tt.in)
if err != nil {
t.Fatal(err)
}

have := strings.TrimSpace(buf.String())
if have != tt.want {
t.Errorf("\nhave:\n%s\nwant:\n%s", have, tt.want)
}
})
}
}

func TestEncodeWithOmitEmpty(t *testing.T) {
type simple struct {
Bool bool `toml:"bool,omitempty"`
String string `toml:"string,omitempty"`
Array [0]byte `toml:"array,omitempty"`
Slice []int `toml:"slice,omitempty"`
Map map[string]string `toml:"map,omitempty"`
Time time.Time `toml:"time,omitempty"`
}

var v simple
Expand All @@ -132,10 +197,12 @@ func TestEncodeWithOmitEmpty(t *testing.T) {
String: " ",
Slice: []int{2, 3, 4},
Map: map[string]string{"foo": "bar"},
Time: time.Date(1985, 6, 18, 15, 16, 17, 0, time.UTC),
}
expected := `bool = true
string = " "
slice = [2, 3, 4]
time = 1985-06-18T15:16:17Z

[map]
foo = "bar"
Expand Down
3 changes: 3 additions & 0 deletions internal/toml-test/json.go
@@ -1,3 +1,6 @@
//go:build go1.16
// +build go1.16

package tomltest

import (
Expand Down
38 changes: 0 additions & 38 deletions internal/toml-test/runner_test.go

This file was deleted.

3 changes: 3 additions & 0 deletions internal/toml-test/toml.go
@@ -1,3 +1,6 @@
//go:build go1.16
// +build go1.16

package tomltest

import (
Expand Down
3 changes: 3 additions & 0 deletions internal/toml-test/version.go
@@ -1,3 +1,6 @@
//go:build go1.16
// +build go1.16

package tomltest

type versionSpec struct {
Expand Down