Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -94,6 +94,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- `omitempty` follows the encoding/json semantics: the field is skipped when
|
||||||
|
it holds an empty string, a zero number, `false`, a nil pointer or
|
||||||
|
interface, or a nil or empty slice, array or map. In 1.x the option covered
|
||||||
|
only the collections.
|
||||||
|
- The `toml` tag gained the `inline` option: a struct or map field tagged
|
||||||
|
`toml:"retry,inline"` emits as `retry = {…}` instead of a header section,
|
||||||
|
whatever its size, a named embedded struct included. Forcing it on an array
|
||||||
|
of tables is an error, because the inline form would re-parse as a value
|
||||||
|
array and change the value's Go type.
|
||||||
- The output takes the TOML 1.1 form. A date-time writes its seconds only when
|
- The output takes the TOML 1.1 form. A date-time writes its seconds only when
|
||||||
the value carries them and drops the trailing zeros of a fractional second,
|
the value carries them and drops the trailing zeros of a fractional second,
|
||||||
so `07:32:00` is written `07:32` and half a second as `00.5`. Both are the
|
so `07:32:00` is written `07:32` and half a second as `00.5`. Both are the
|
||||||
|
|||||||
+13
-5
@@ -475,22 +475,30 @@ nil map emits nothing.
|
|||||||
|
|
||||||
### Tag options
|
### Tag options
|
||||||
|
|
||||||
The part of a `toml` tag after the first comma carries options. Both options
|
The part of a `toml` tag after the first comma carries options. They shape
|
||||||
shape emission only; the decoder ignores them.
|
emission only; the decoder ignores them, so a value that round-trips keeps
|
||||||
|
its key whether the table it came from was written inline or under a header.
|
||||||
|
|
||||||
- `omitzero` skips the field when its value is the zero value of its type. A
|
- `omitzero` skips the field when its value is the zero value of its type. A
|
||||||
type with an `IsZero() bool` method (time.Time among them) decides through
|
type with an `IsZero() bool` method (time.Time among them) decides through
|
||||||
that method, so a zero `time.Time` or an all-zero struct disappears from
|
that method, so a zero `time.Time` or an all-zero struct disappears from
|
||||||
the output.
|
the output.
|
||||||
- `omitempty` skips the field when it holds an empty collection: a nil or
|
- `omitempty` skips the field when it holds an empty value in the
|
||||||
empty slice or array, or a nil or empty map. Strings and other scalars are
|
encoding/json sense: an empty string, a zero number, `false`, a nil pointer
|
||||||
not covered by `omitempty`; use `omitzero` for those.
|
or interface, and a nil or empty slice, array or map. This is a change of
|
||||||
|
semantics against 1.x, where only collections were covered.
|
||||||
|
- `inline` forces a struct or map field to emit as `name = {…}`, the inline
|
||||||
|
table form, instead of a header section, whatever its size; a named
|
||||||
|
embedded struct tagged this way does the same. A field holding an array of
|
||||||
|
tables is an error under `inline`, because the inline form would re-parse
|
||||||
|
as a value array and change the value's Go type.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Host string `toml:"host,omitzero"`
|
Host string `toml:"host,omitzero"`
|
||||||
Started time.Time `toml:"started,omitzero"`
|
Started time.Time `toml:"started,omitzero"`
|
||||||
Tags []string `toml:"tags,omitempty"`
|
Tags []string `toml:"tags,omitempty"`
|
||||||
|
Retry Retry `toml:"retry,inline"`
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ func buildOrderedDoc(om *OrderedMap, doc *tomlDoc, path encPath) error {
|
|||||||
// A nil value has no TOML form, the rule nil pointer fields follow.
|
// A nil value has no TOML form, the rule nil pointer fields follow.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := addField(doc, key, rv, path); err != nil {
|
if err := addField(doc, key, rv, path, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,6 +304,10 @@ type entry struct {
|
|||||||
doc *tomlDoc // entryTable
|
doc *tomlDoc // entryTable
|
||||||
docs []*tomlDoc
|
docs []*tomlDoc
|
||||||
|
|
||||||
|
// inline forces a table entry to emit as `key = {…}`; it is set by the
|
||||||
|
// `,inline` tag option. An array of tables keeps the header form.
|
||||||
|
inline bool
|
||||||
|
|
||||||
// emitted records that the grouped emission wrote this table inline, so
|
// emitted records that the grouped emission wrote this table inline, so
|
||||||
// the header pass that follows skips it. The representation is built
|
// the header pass that follows skips it. The representation is built
|
||||||
// fresh per Marshal call.
|
// fresh per Marshal call.
|
||||||
@@ -347,8 +351,10 @@ func (d *tomlDoc) addScalar(key string, val any) {
|
|||||||
d.entries = append(d.entries, entry{kind: entryScalar, key: key, val: val})
|
d.entries = append(d.entries, entry{kind: entryScalar, key: key, val: val})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *tomlDoc) addTable(key string, sub *tomlDoc) {
|
func (d *tomlDoc) addTable(key string, sub *tomlDoc) *entry {
|
||||||
d.entries = append(d.entries, entry{kind: entryTable, key: key, doc: sub})
|
e := entry{kind: entryTable, key: key, doc: sub}
|
||||||
|
d.entries = append(d.entries, e)
|
||||||
|
return &d.entries[len(d.entries)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *tomlDoc) addArray(key string, subs []*tomlDoc) {
|
func (d *tomlDoc) addArray(key string, subs []*tomlDoc) {
|
||||||
@@ -482,7 +488,7 @@ func walkStructDoc(v reflect.Value, doc *tomlDoc, path encPath, prefix []int, sc
|
|||||||
if fieldOmitted(f, v.Field(i)) {
|
if fieldOmitted(f, v.Field(i)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := addField(doc, name, v.Field(i), path); err != nil {
|
if err := addField(doc, name, v.Field(i), path, tagHasOption(f.Tag.Get("toml"), "inline")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -493,30 +499,69 @@ func walkStructDoc(v reflect.Value, doc *tomlDoc, path encPath, prefix []int, sc
|
|||||||
// state decides through that method before reflection is consulted.
|
// state decides through that method before reflection is consulted.
|
||||||
type isZeroer interface{ IsZero() bool }
|
type isZeroer interface{ IsZero() bool }
|
||||||
|
|
||||||
|
// tagOptions returns the option part of a `toml` tag, the part after the
|
||||||
|
// first comma.
|
||||||
|
func tagOptions(tag string) string {
|
||||||
|
_, opts, _ := strings.Cut(tag, ",")
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// tagHasOption reports whether want is one of the tag's comma-separated
|
||||||
|
// options.
|
||||||
|
func tagHasOption(tag, want string) bool {
|
||||||
|
opts := tagOptions(tag)
|
||||||
|
for opts != "" {
|
||||||
|
var opt string
|
||||||
|
opt, opts, _ = strings.Cut(opts, ",")
|
||||||
|
if opt == want {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// fieldOmitted reports whether the field's tag options drop it from the
|
// fieldOmitted reports whether the field's tag options drop it from the
|
||||||
// output: omitzero skips the zero value of the field's type, omitempty skips
|
// output: omitzero skips the zero value of the field's type, omitempty skips
|
||||||
// an empty collection (slice, array, or map). The decoder ignores both
|
// an empty value in the encoding/json sense, an empty string, a zero number,
|
||||||
// options; they shape emission only.
|
// false, a nil pointer or interface, and an empty slice, array or map. The
|
||||||
|
// decoder ignores both options; they shape emission only.
|
||||||
func fieldOmitted(f reflect.StructField, v reflect.Value) bool {
|
func fieldOmitted(f reflect.StructField, v reflect.Value) bool {
|
||||||
tag, ok := f.Tag.Lookup("toml")
|
tag, ok := f.Tag.Lookup("toml")
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
_, opts, _ := strings.Cut(tag, ",")
|
if tagHasOption(tag, "omitzero") && isZeroValue(v) {
|
||||||
for opts != "" {
|
return true
|
||||||
var opt string
|
}
|
||||||
opt, opts, _ = strings.Cut(opts, ",")
|
if tagHasOption(tag, "omitempty") {
|
||||||
switch opt {
|
switch v.Kind() {
|
||||||
case "omitzero":
|
case reflect.Slice, reflect.Array, reflect.Map:
|
||||||
if isZeroValue(v) {
|
if v.Len() == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case "omitempty":
|
case reflect.String:
|
||||||
switch v.Kind() {
|
if v.Len() == 0 {
|
||||||
case reflect.Slice, reflect.Array, reflect.Map:
|
return true
|
||||||
if v.Len() == 0 {
|
}
|
||||||
return true
|
case reflect.Bool:
|
||||||
}
|
if !v.Bool() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
if v.Int() == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
if v.Uint() == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
if v.Float() == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case reflect.Pointer, reflect.Interface:
|
||||||
|
if v.IsNil() {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -569,7 +614,7 @@ func buildMapDoc(v reflect.Value, doc *tomlDoc, path encPath) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := addField(doc, k.String(), v.MapIndex(k), path); err != nil {
|
if err := addField(doc, k.String(), v.MapIndex(k), path, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -584,7 +629,11 @@ func buildMapDoc(v reflect.Value, doc *tomlDoc, path encPath) error {
|
|||||||
// contract violation.
|
// contract violation.
|
||||||
var errNilMarshalTOML = errors.New("MarshalTOML returned a nil value")
|
var errNilMarshalTOML = errors.New("MarshalTOML returned a nil value")
|
||||||
|
|
||||||
func addField(doc *tomlDoc, name string, v reflect.Value, path encPath) error {
|
// addField adds one value under name, the shape addField picks deciding
|
||||||
|
// whether it is a scalar line, a sub-table or an array. forceInline marks a
|
||||||
|
// table-valued field carrying the `,inline` tag option: it emits as
|
||||||
|
// `key = {…}` instead of a header section.
|
||||||
|
func addField(doc *tomlDoc, name string, v reflect.Value, path encPath, forceInline bool) error {
|
||||||
if m, ok := marshalerOf(v); ok {
|
if m, ok := marshalerOf(v); ok {
|
||||||
mv, err := m.MarshalTOML()
|
mv, err := m.MarshalTOML()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -624,7 +673,7 @@ func addField(doc *tomlDoc, name string, v reflect.Value, path encPath) error {
|
|||||||
if err := buildOrderedDoc(&om, sub, path.key(name)); err != nil {
|
if err := buildOrderedDoc(&om, sub, path.key(name)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
doc.addTable(name, sub)
|
doc.addTable(name, sub).inline = forceInline
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
@@ -633,11 +682,11 @@ func addField(doc *tomlDoc, name string, v reflect.Value, path encPath) error {
|
|||||||
doc.addScalar(name, v.Interface())
|
doc.addScalar(name, v.Interface())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return addSubTable(doc, name, v, path)
|
return addSubTable(doc, name, v, path, forceInline)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
return addSubTable(doc, name, v, path)
|
return addSubTable(doc, name, v, path, forceInline)
|
||||||
case reflect.Slice, reflect.Array:
|
case reflect.Slice, reflect.Array:
|
||||||
return addArrayValue(doc, name, v, path)
|
return addArrayValue(doc, name, v, path, forceInline)
|
||||||
default:
|
default:
|
||||||
val, err := normaliseValue(v)
|
val, err := normaliseValue(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -648,7 +697,7 @@ func addField(doc *tomlDoc, name string, v reflect.Value, path encPath) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addSubTable(doc *tomlDoc, name string, v reflect.Value, path encPath) error {
|
func addSubTable(doc *tomlDoc, name string, v reflect.Value, path encPath, forceInline bool) error {
|
||||||
sub := &tomlDoc{ctx: doc.ctx, opts: doc.opts, depth: doc.depth + 1}
|
sub := &tomlDoc{ctx: doc.ctx, opts: doc.opts, depth: doc.depth + 1}
|
||||||
if atDepthLimit(sub.depth) {
|
if atDepthLimit(sub.depth) {
|
||||||
return &EncodeError{Path: path.key(name).String(), Err: errDepthLimit()}
|
return &EncodeError{Path: path.key(name).String(), Err: errDepthLimit()}
|
||||||
@@ -663,11 +712,11 @@ func addSubTable(doc *tomlDoc, name string, v reflect.Value, path encPath) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
doc.addTable(name, sub)
|
doc.addTable(name, sub).inline = forceInline
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addArrayValue(doc *tomlDoc, name string, v reflect.Value, path encPath) error {
|
func addArrayValue(doc *tomlDoc, name string, v reflect.Value, path encPath, forceInline bool) error {
|
||||||
if v.Kind() == reflect.Slice && v.IsNil() {
|
if v.Kind() == reflect.Slice && v.IsNil() {
|
||||||
// A nil slice has no explicit representation in TOML, so it is skipped.
|
// A nil slice has no explicit representation in TOML, so it is skipped.
|
||||||
return nil
|
return nil
|
||||||
@@ -723,6 +772,11 @@ func addArrayValue(doc *tomlDoc, name string, v reflect.Value, path encPath) err
|
|||||||
if v.Type().Elem().Kind() == reflect.Interface {
|
if v.Type().Elem().Kind() == reflect.Interface {
|
||||||
allTables = false
|
allTables = false
|
||||||
}
|
}
|
||||||
|
// A `,inline` tag on an array of tables asks for a form that would change
|
||||||
|
// the value's Go type on re-parse, so the error is the honest answer.
|
||||||
|
if forceInline && allTables {
|
||||||
|
return &EncodeError{Path: path.key(name).String(), Err: errors.New("an array of tables has no inline form")}
|
||||||
|
}
|
||||||
if allTables {
|
if allTables {
|
||||||
subs := make([]*tomlDoc, n)
|
subs := make([]*tomlDoc, n)
|
||||||
for i, ev := range elems {
|
for i, ev := range elems {
|
||||||
@@ -1104,7 +1158,7 @@ func (e *encoder) emitDoc(doc *tomlDoc, prefix []string) error {
|
|||||||
if t.kind != entryTable {
|
if t.kind != entryTable {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
inlined, err := e.writeInlineSubTableIfSmall(t.key, t.doc)
|
inlined, err := e.writeInlineSubTableIfSmall(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1159,7 +1213,7 @@ func (e *encoder) emitDoc(doc *tomlDoc, prefix []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case entryTable:
|
case entryTable:
|
||||||
inlined, err := e.writeInlineSubTableIfSmall(ent.key, ent.doc)
|
inlined, err := e.writeInlineSubTableIfSmall(&ent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1516,29 +1570,34 @@ func (e *encoder) writeInlineDocMultiline(doc *tomlDoc) error {
|
|||||||
|
|
||||||
// writeInlineSubTableIfSmall writes "key = {…}" for a sub-table whose
|
// writeInlineSubTableIfSmall writes "key = {…}" for a sub-table whose
|
||||||
// single-line rendering fits the compact threshold, and reports whether it did
|
// single-line rendering fits the compact threshold, and reports whether it did
|
||||||
// so. An array of tables is never inlined, because its inline form would
|
// so. An entry the `,inline` tag option marks is written regardless of the
|
||||||
// re-parse as a value array and change the value's Go type.
|
// threshold. An array of tables is never inlined, because its inline form
|
||||||
func (e *encoder) writeInlineSubTableIfSmall(name string, doc *tomlDoc) (bool, error) {
|
// would re-parse as a value array and change the value's Go type; a forced
|
||||||
if e.opts.inlineTablesAt <= 0 || !inlinableDoc(doc) {
|
// inline of one is an error rather than a silent form change.
|
||||||
|
func (e *encoder) writeInlineSubTableIfSmall(t *entry) (bool, error) {
|
||||||
|
if !t.inline && (e.opts.inlineTablesAt <= 0 || !inlinableDoc(t.doc)) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
if !inlinableDoc(t.doc) {
|
||||||
|
return false, fmt.Errorf("interpres: field %q holds an array of tables and has no inline form", t.key)
|
||||||
|
}
|
||||||
flat := e.flat()
|
flat := e.flat()
|
||||||
if err := flat.writeInlineDoc(doc); err != nil {
|
if err := flat.writeInlineDoc(t.doc); err != nil {
|
||||||
flat.release()
|
flat.release()
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if flat.buf.Len() > e.opts.inlineTablesAt {
|
if !t.inline && flat.buf.Len() > e.opts.inlineTablesAt {
|
||||||
flat.release()
|
flat.release()
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
if err := e.writeKey(name); err != nil {
|
if err := e.writeKey(t.key); err != nil {
|
||||||
flat.release()
|
flat.release()
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
e.buf.WriteString(" = ")
|
e.buf.WriteString(" = ")
|
||||||
if e.column()+flat.buf.Len() <= e.limit {
|
if e.column()+flat.buf.Len() <= e.limit {
|
||||||
e.buf.Write(flat.buf.Bytes())
|
e.buf.Write(flat.buf.Bytes())
|
||||||
} else if err := e.writeInlineDocMultiline(doc); err != nil {
|
} else if err := e.writeInlineDocMultiline(t.doc); err != nil {
|
||||||
flat.release()
|
flat.release()
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|||||||
+108
@@ -2126,3 +2126,111 @@ func TestUnmarshalWithOptions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInlineTag(t *testing.T) {
|
||||||
|
type Inner struct {
|
||||||
|
A int `toml:"a"`
|
||||||
|
B int `toml:"b"`
|
||||||
|
}
|
||||||
|
t.Run("a struct field writes inline", func(t *testing.T) {
|
||||||
|
type Cfg struct {
|
||||||
|
Inner Inner `toml:"inner,inline"`
|
||||||
|
}
|
||||||
|
out, err := Marshal(Cfg{Inner: Inner{1, 2}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(out) != "inner = {a = 1, b = 2}\n" {
|
||||||
|
t.Errorf("output %q", out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("a map field writes inline", func(t *testing.T) {
|
||||||
|
type Cfg struct {
|
||||||
|
Opts map[string]int `toml:"opts,inline"`
|
||||||
|
}
|
||||||
|
out, err := Marshal(Cfg{Opts: map[string]int{"x": 1}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(out) != "opts = {x = 1}\n" {
|
||||||
|
t.Errorf("output %q", out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("a named embedded struct writes inline", func(t *testing.T) {
|
||||||
|
type Cfg struct {
|
||||||
|
Inner `toml:"inner,inline"`
|
||||||
|
}
|
||||||
|
out, err := Marshal(Cfg{Inner: Inner{1, 2}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(out) != "inner = {a = 1, b = 2}\n" {
|
||||||
|
t.Errorf("output %q", out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("an inline field decodes back", func(t *testing.T) {
|
||||||
|
type Cfg struct {
|
||||||
|
Inner Inner `toml:"inner,inline"`
|
||||||
|
}
|
||||||
|
var cfg Cfg
|
||||||
|
if err := Unmarshal([]byte("inner = {a = 3, b = 4}\n"), &cfg); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if cfg.Inner != (Inner{3, 4}) {
|
||||||
|
t.Errorf("decoded %+v", cfg.Inner)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("a forced inline of an array of tables is an error", func(t *testing.T) {
|
||||||
|
type Item struct {
|
||||||
|
N int `toml:"n"`
|
||||||
|
}
|
||||||
|
type Cfg struct {
|
||||||
|
Items []Item `toml:"items,inline"`
|
||||||
|
}
|
||||||
|
if _, err := Marshal(Cfg{Items: []Item{{1}}}); err == nil {
|
||||||
|
t.Error("forced inline of an array of tables succeeded, want an error")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("without the tag the header form stands", func(t *testing.T) {
|
||||||
|
type Cfg struct {
|
||||||
|
Inner Inner `toml:"inner"`
|
||||||
|
}
|
||||||
|
out, err := Marshal(Cfg{Inner: Inner{1, 2}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(out) != "[inner]\na = 1\nb = 2\n" {
|
||||||
|
t.Errorf("output %q", out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOmitEmptyJSONSemantics(t *testing.T) {
|
||||||
|
type Cfg struct {
|
||||||
|
Empty string `toml:"empty,omitempty"`
|
||||||
|
Full string `toml:"full,omitempty"`
|
||||||
|
Zero int `toml:"zero,omitempty"`
|
||||||
|
One int `toml:"one,omitempty"`
|
||||||
|
Off bool `toml:"off,omitempty"`
|
||||||
|
On bool `toml:"on,omitempty"`
|
||||||
|
Nil *string `toml:"nil,omitempty"`
|
||||||
|
Set *string `toml:"set,omitempty"`
|
||||||
|
Nothing map[string]string `toml:"nothing,omitempty"`
|
||||||
|
Somethg map[string]string `toml:"somethg,omitempty"`
|
||||||
|
}
|
||||||
|
s := "x"
|
||||||
|
out, err := Marshal(Cfg{
|
||||||
|
Full: "y",
|
||||||
|
One: 1,
|
||||||
|
On: true,
|
||||||
|
Set: &s,
|
||||||
|
Somethg: map[string]string{"k": "v"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
want := "full = \"y\"\none = 1\non = true\nset = \"x\"\n\n[somethg]\nk = \"v\"\n"
|
||||||
|
if string(out) != want {
|
||||||
|
t.Errorf("output:\n%q\nwant:\n%q", out, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user