feat: add field comments, local time zone decoding and in-value cancellation
Test / test (push) Successful in 1m32s

Assisted-by: GLM 5.3 Flash
This commit is contained in:
2026-09-22 00:44:23 +02:00
parent 71bd82a7a5
commit d18935ebc2
8 changed files with 307 additions and 11 deletions
+53 -7
View File
@@ -308,6 +308,11 @@ type entry struct {
// `,inline` tag option. An array of tables keeps the header form.
inline bool
// comments are the comment lines written above this entry's line or
// header, which the `comment=` tag option carries when
// Encoder.EmitFieldComments is on.
comments []string
// emitted records that the grouped emission wrote this table inline, so
// the header pass that follows skips it. The representation is built
// fresh per Marshal call.
@@ -482,9 +487,17 @@ func walkStructDoc(v reflect.Value, doc *tomlDoc, path encPath, prefix []int, sc
if fieldOmitted(f, v.Field(i)) {
continue
}
before := len(doc.entries)
if err := addField(doc, name, v.Field(i), path, tagHasOption(f.Tag.Get("toml"), "inline")); err != nil {
return err
}
// The comment a `comment=` tag option carries lands on the entry the
// field emitted, when the option to print field comments is on.
if len(doc.entries) > before && doc.opts.emitFieldComments {
if text := tagComment(f.Tag.Get("toml")); text != "" {
doc.entries[len(doc.entries)-1].comments = strings.Split(text, "\n")
}
}
}
return nil
}
@@ -514,6 +527,20 @@ func tagHasOption(tag, want string) bool {
return false
}
// tagComment returns the text a `comment=` tag option carries, without the
// option name. An unset comment comes back empty.
func tagComment(tag string) string {
opts := tagOptions(tag)
for opts != "" {
var opt string
opt, opts, _ = strings.Cut(opts, ",")
if text, ok := strings.CutPrefix(opt, "comment="); ok {
return text
}
}
return ""
}
// 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
// an empty value in the encoding/json sense, an empty string, a zero number,
@@ -1140,7 +1167,7 @@ func (e *encoder) emitDoc(doc *tomlDoc, prefix []string) error {
if kv.kind != entryScalar {
continue
}
if err := e.writeKV(kv.key, kv.val); err != nil {
if err := e.writeKV(kv); err != nil {
return err
}
}
@@ -1165,6 +1192,7 @@ func (e *encoder) emitDoc(doc *tomlDoc, prefix []string) error {
}
path := append(append([]string{}, prefix...), t.key)
e.writeBlankLine()
e.writeComments(t.comments)
e.buf.WriteByte('[')
if err := e.writeKeyPath(path); err != nil {
return err
@@ -1180,8 +1208,11 @@ func (e *encoder) emitDoc(doc *tomlDoc, prefix []string) error {
continue
}
path := append(append([]string{}, prefix...), a.key)
for _, sub := range a.docs {
for j, sub := range a.docs {
e.writeBlankLine()
if j == 0 {
e.writeComments(a.comments)
}
e.buf.WriteString("[[")
if err := e.writeKeyPath(path); err != nil {
return err
@@ -1203,7 +1234,7 @@ func (e *encoder) emitDoc(doc *tomlDoc, prefix []string) error {
for _, ent := range doc.entries {
switch ent.kind {
case entryScalar:
if err := e.writeKV(ent.key, ent.val); err != nil {
if err := e.writeKV(&ent); err != nil {
return err
}
case entryTable:
@@ -1216,6 +1247,7 @@ func (e *encoder) emitDoc(doc *tomlDoc, prefix []string) error {
}
path := append(append([]string{}, prefix...), ent.key)
e.writeBlankLine()
e.writeComments(ent.comments)
e.buf.WriteByte('[')
if err := e.writeKeyPath(path); err != nil {
return err
@@ -1226,8 +1258,11 @@ func (e *encoder) emitDoc(doc *tomlDoc, prefix []string) error {
}
case entryArray:
path := append(append([]string{}, prefix...), ent.key)
for _, sub := range ent.docs {
for j, sub := range ent.docs {
e.writeBlankLine()
if j == 0 {
e.writeComments(ent.comments)
}
e.buf.WriteString("[[")
if err := e.writeKeyPath(path); err != nil {
return err
@@ -1242,12 +1277,23 @@ func (e *encoder) emitDoc(doc *tomlDoc, prefix []string) error {
return nil
}
func (e *encoder) writeKV(key string, val any) error {
if err := e.writeKey(key); err != nil {
// writeComments writes comment lines above an entry, each prefixed with the
// "# " marker the parser strips on the way in.
func (e *encoder) writeComments(lines []string) {
for _, line := range lines {
e.buf.WriteString("# ")
e.buf.WriteString(line)
e.buf.WriteByte('\n')
}
}
func (e *encoder) writeKV(ent *entry) error {
e.writeComments(ent.comments)
if err := e.writeKey(ent.key); err != nil {
return err
}
e.buf.WriteString(" = ")
if err := e.writeValue(val); err != nil {
if err := e.writeValue(ent.val); err != nil {
return err
}
e.buf.WriteByte('\n')