feat(encode): add the InlineTables option
Assisted-by: DeepSeek V4.1 Flash
This commit is contained in:
@@ -741,7 +741,20 @@ func (e *encoder) emitDoc(doc *tomlDoc, prefix []string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// An inlined sub-table is a value line, so it has to precede every
|
||||
// header of this document: a line written after a [header] would be
|
||||
// read back as part of that table.
|
||||
headers := make([]entry, 0, len(tables))
|
||||
for _, t := range tables {
|
||||
inlined, err := e.writeInlineSubTableIfSmall(t.key, t.doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !inlined {
|
||||
headers = append(headers, t)
|
||||
}
|
||||
}
|
||||
for _, t := range headers {
|
||||
path := append(append([]string{}, prefix...), t.key)
|
||||
e.writeBlankLine()
|
||||
e.buf.WriteByte('[')
|
||||
@@ -782,6 +795,13 @@ func (e *encoder) emitDoc(doc *tomlDoc, prefix []string) error {
|
||||
return err
|
||||
}
|
||||
case entryTable:
|
||||
inlined, err := e.writeInlineSubTableIfSmall(ent.key, ent.doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if inlined {
|
||||
continue
|
||||
}
|
||||
path := append(append([]string{}, prefix...), ent.key)
|
||||
e.writeBlankLine()
|
||||
e.buf.WriteByte('[')
|
||||
@@ -1021,6 +1041,107 @@ func (e *encoder) writeInlineIndent() {
|
||||
}
|
||||
}
|
||||
|
||||
// errInlineArrayOfTables reports an attempt to render an array of tables
|
||||
// inline, which has no form that keeps the value's type.
|
||||
var errInlineArrayOfTables = errors.New("interpres: an array of tables has no inline form")
|
||||
|
||||
// inlinableDoc reports whether doc can be written as an inline table without
|
||||
// changing the type of any value: scalars, value arrays and further sub-tables
|
||||
// are fine, while an array of tables is not, because its inline form would
|
||||
// re-parse as a value array.
|
||||
func inlinableDoc(doc *tomlDoc) bool {
|
||||
for _, ent := range doc.entries {
|
||||
switch ent.kind {
|
||||
case entryArray:
|
||||
return false
|
||||
case entryTable:
|
||||
if !inlinableDoc(ent.doc) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// writeInlineDocEntry writes one "key = value" binding of an inline table,
|
||||
// without the separator that follows it.
|
||||
func (e *encoder) writeInlineDocEntry(ent entry) error {
|
||||
if err := e.writeKey(ent.key); err != nil {
|
||||
return err
|
||||
}
|
||||
e.buf.WriteString(" = ")
|
||||
switch ent.kind {
|
||||
case entryTable:
|
||||
return e.writeInlineDoc(ent.doc)
|
||||
case entryArray:
|
||||
return errInlineArrayOfTables
|
||||
default:
|
||||
return e.writeValue(ent.val)
|
||||
}
|
||||
}
|
||||
|
||||
// writeInlineDoc renders doc as a single-line inline table in entry order, the
|
||||
// order the fields were declared in.
|
||||
func (e *encoder) writeInlineDoc(doc *tomlDoc) error {
|
||||
e.buf.WriteByte('{')
|
||||
for i, ent := range doc.entries {
|
||||
if i > 0 {
|
||||
e.buf.WriteString(", ")
|
||||
}
|
||||
if err := e.writeInlineDocEntry(ent); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
e.buf.WriteByte('}')
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeInlineDocMultiline renders doc with one entry per line and a trailing
|
||||
// comma, the form TOML 1.1 allows for an inline table too long for one line.
|
||||
func (e *encoder) writeInlineDocMultiline(doc *tomlDoc) error {
|
||||
e.buf.WriteString("{\n")
|
||||
e.inlineDepth++
|
||||
for _, ent := range doc.entries {
|
||||
e.writeInlineIndent()
|
||||
if err := e.writeInlineDocEntry(ent); err != nil {
|
||||
return err
|
||||
}
|
||||
e.buf.WriteString(",\n")
|
||||
}
|
||||
e.inlineDepth--
|
||||
e.writeInlineIndent()
|
||||
e.buf.WriteByte('}')
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeInlineSubTableIfSmall writes "key = {…}" for a sub-table whose
|
||||
// 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
|
||||
// re-parse as a value array and change the value's Go type.
|
||||
func (e *encoder) writeInlineSubTableIfSmall(name string, doc *tomlDoc) (bool, error) {
|
||||
if e.opts.inlineTablesAt <= 0 || !inlinableDoc(doc) {
|
||||
return false, nil
|
||||
}
|
||||
flat := e.flat()
|
||||
if err := flat.writeInlineDoc(doc); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if flat.buf.Len() > e.opts.inlineTablesAt {
|
||||
return false, nil
|
||||
}
|
||||
if err := e.writeKey(name); err != nil {
|
||||
return false, err
|
||||
}
|
||||
e.buf.WriteString(" = ")
|
||||
if e.column()+flat.buf.Len() <= e.limit {
|
||||
e.buf.Write(flat.buf.Bytes())
|
||||
} else if err := e.writeInlineDocMultiline(doc); err != nil {
|
||||
return false, err
|
||||
}
|
||||
e.buf.WriteByte('\n')
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (e *encoder) writeStringVal(s string) error {
|
||||
if e.opts.literalMultilineAt > 0 && strings.ContainsRune(s, '\n') &&
|
||||
len(s) >= e.opts.literalMultilineAt && canBeLiteralMultiline(s) {
|
||||
|
||||
Reference in New Issue
Block a user