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

Encode: define and fix newlines behavior when using omitempty #798

Merged
merged 2 commits into from Jul 24, 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
1 change: 0 additions & 1 deletion cmd/jsontoml/main_test.go
Expand Up @@ -26,7 +26,6 @@ func TestConvert(t *testing.T) {
}`,
expected: `[mytoml]
a = 42.0

`,
},
{
Expand Down
1 change: 0 additions & 1 deletion cmd/tomll/main_test.go
Expand Up @@ -23,7 +23,6 @@ mytoml.a = 42.0
`,
expected: `[mytoml]
a = 42.0

`,
},
{
Expand Down
9 changes: 5 additions & 4 deletions internal/imported_tests/marshal_imported_test.go
Expand Up @@ -67,6 +67,7 @@ func TestDocMarshal(t *testing.T) {
}

marshalTestToml := `title = 'TOML Marshal Testing'

[basic_lists]
floats = [12.3, 45.6, 78.9]
bools = [true, false, true]
Expand All @@ -89,7 +90,6 @@ name = 'Second'
[subdoc.first]
name = 'First'


[basic]
uint = 5001
bool = true
Expand All @@ -101,9 +101,9 @@ date = 1979-05-27T07:32:00Z

[[subdoclist]]
name = 'List.First'

[[subdoclist]]
name = 'List.Second'

`

result, err := toml.Marshal(docData)
Expand All @@ -117,14 +117,15 @@ func TestBasicMarshalQuotedKey(t *testing.T) {

expected := `'Z.string-àéù' = 'Hello'
'Yfloat-𝟘' = 3.5

['Xsubdoc-àéù']
String2 = 'One'

[['W.sublist-𝟘']]
String2 = 'Two'

[['W.sublist-𝟘']]
String2 = 'Three'

`

require.Equal(t, string(expected), string(result))
Expand Down Expand Up @@ -159,8 +160,8 @@ bool = false
int = 0
string = ''
stringlist = []
[map]

[map]
`

require.Equal(t, string(expected), string(result))
Expand Down
85 changes: 76 additions & 9 deletions marshaler.go
Expand Up @@ -54,7 +54,7 @@ func NewEncoder(w io.Writer) *Encoder {
// This behavior can be controlled on an individual struct field basis with the
// inline tag:
//
// MyField `inline:"true"`
// MyField `toml:",inline"`
func (enc *Encoder) SetTablesInline(inline bool) *Encoder {
enc.tablesInline = inline
return enc
Expand Down Expand Up @@ -117,6 +117,19 @@ func (enc *Encoder) SetIndentTables(indent bool) *Encoder {
// When encoding structs, fields are encoded in order of definition, with their
// exact name.
//
// Tables and array tables are separated by empty lines. However, consecutive
// subtables definitions are not. For example:
//
// [top1]
//
// [top2]
// [top2.child1]
//
// [[array]]
//
// [[array]]
// [array.child2]
//
// Struct tags
//
// The encoding of each public struct field can be customized by the format
Expand Down Expand Up @@ -333,13 +346,13 @@ func isNil(v reflect.Value) bool {
}
}

func shouldOmitEmpty(ctx encoderCtx, options valueOptions, v reflect.Value) bool {
return (ctx.options.omitempty || options.omitempty) && isEmptyValue(v)
}

func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) {
var err error

if (ctx.options.omitempty || options.omitempty) && isEmptyValue(v) {
return b, nil
}

if !ctx.inline {
b = enc.encodeComment(ctx.indent, options.comment, b)
}
Expand All @@ -365,6 +378,8 @@ func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v r

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:
Expand All @@ -381,6 +396,34 @@ func isEmptyValue(v reflect.Value) bool {
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 {
Expand Down Expand Up @@ -410,7 +453,6 @@ func (enc *Encoder) encodeLiteralString(b []byte, v string) []byte {
return b
}

//nolint:cyclop
func (enc *Encoder) encodeQuotedString(multiline bool, b []byte, v string) []byte {
stringQuote := `"`

Expand Down Expand Up @@ -757,7 +799,13 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro
}
ctx.skipTableHeader = false

hasNonEmptyKV := false
for _, kv := range t.kvs {
if shouldOmitEmpty(ctx, kv.Options, kv.Value) {
continue
}
hasNonEmptyKV = true

ctx.setKey(kv.Key)

b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value)
Expand All @@ -768,7 +816,20 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro
b = append(b, '\n')
}

first := true
for _, table := range t.tables {
if shouldOmitEmpty(ctx, table.Options, table.Value) {
continue
}
if first {
first = false
if hasNonEmptyKV {
b = append(b, '\n')
}
} else {
b = append(b, "\n"...)
}

ctx.setKey(table.Key)

ctx.options = table.Options
Expand All @@ -777,8 +838,6 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro
if err != nil {
return nil, err
}

b = append(b, '\n')
}

return b, nil
Expand All @@ -791,6 +850,10 @@ func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte

first := true
for _, kv := range t.kvs {
if shouldOmitEmpty(ctx, kv.Options, kv.Value) {
continue
}

if first {
first = false
} else {
Expand All @@ -806,7 +869,7 @@ func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte
}

if len(t.tables) > 0 {
panic("inline table cannot contain nested tables, online key-values")
panic("inline table cannot contain nested tables, only key-values")
}

b = append(b, "}"...)
Expand Down Expand Up @@ -905,6 +968,10 @@ func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.
b = enc.encodeComment(ctx.indent, ctx.options.comment, b)

for i := 0; i < v.Len(); i++ {
if i != 0 {
b = append(b, "\n"...)
}

b = append(b, scratch...)

var err error
Expand Down