From b36a38a7bbfe12e1d70d5b23ba226a6ef31f7c58 Mon Sep 17 00:00:00 2001 From: Nathan Baulch Date: Fri, 30 May 2025 22:05:46 +1000 Subject: [PATCH] Use reflect.Value.IsZero for omitempty checks --- marshaler.go | 50 +---------------------------------------------- marshaler_test.go | 22 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 49 deletions(-) diff --git a/marshaler.go b/marshaler.go index 161acd93..2d3d7808 100644 --- a/marshaler.go +++ b/marshaler.go @@ -381,7 +381,7 @@ func isNil(v reflect.Value) bool { } func shouldOmitEmpty(options valueOptions, v reflect.Value) bool { - return options.omitempty && isEmptyValue(v) + return options.omitempty && v.IsZero() } func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) { @@ -418,54 +418,6 @@ func (enc *Encoder) commented(commented bool, b []byte) []byte { return b } -func isEmptyValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.Struct: - return isEmptyStruct(v) - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - } - return false -} - -func isEmptyStruct(v reflect.Value) bool { - // TODO: merge with walkStruct and cache. - typ := v.Type() - for i := 0; i < typ.NumField(); i++ { - fieldType := typ.Field(i) - - // only consider exported fields - if fieldType.PkgPath != "" { - continue - } - - tag := fieldType.Tag.Get("toml") - - // special field name to skip field - if tag == "-" { - continue - } - - f := v.Field(i) - - if !isEmptyValue(f) { - return false - } - } - - return true -} - const literalQuote = '\'' func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byte { diff --git a/marshaler_test.go b/marshaler_test.go index 9c069a04..2133b930 100644 --- a/marshaler_test.go +++ b/marshaler_test.go @@ -6,6 +6,7 @@ import ( "fmt" "math" "math/big" + "net/netip" "reflect" "strings" "testing" @@ -1069,6 +1070,8 @@ func TestEncoderOmitempty(t *testing.T) { Ptr *string `toml:",omitempty,multiline"` Iface interface{} `toml:",omitempty,multiline"` Struct struct{} `toml:",omitempty,multiline"` + Time time.Time `toml:",omitempty,multiline"` + IP netip.Addr `toml:",omitempty,multiline"` } d := doc{} @@ -1081,6 +1084,25 @@ func TestEncoderOmitempty(t *testing.T) { assert.Equal(t, expected, string(b)) } +func TestEncoderOmitemptyComparableStruct(t *testing.T) { + type doc struct { + Time time.Time `toml:",omitempty"` + IP netip.Addr `toml:",omitempty"` + } + + d := doc{ + Time: time.Date(2001, 2, 3, 4, 5, 6, 7, time.UTC), + IP: netip.MustParseAddr("192.168.178.35"), + } + + b, err := toml.Marshal(d) + assert.NoError(t, err) + + expected := "Time = 2001-02-03T04:05:06.000000007Z\nIP = '192.168.178.35'\n" + + assert.Equal(t, expected, string(b)) +} + func TestEncoderTagFieldName(t *testing.T) { type doc struct { String string `toml:"hello"`