mirror

Mirror free and open-source projects you like with minimal effort
git clone git://git.server.ky/slackcoder/mirror
Log | Files | Refs | README

encode.go (19282B)


      1 package toml
      2 
      3 import (
      4 	"bufio"
      5 	"bytes"
      6 	"encoding"
      7 	"encoding/json"
      8 	"errors"
      9 	"fmt"
     10 	"io"
     11 	"math"
     12 	"reflect"
     13 	"sort"
     14 	"strconv"
     15 	"strings"
     16 	"time"
     17 
     18 	"github.com/BurntSushi/toml/internal"
     19 )
     20 
     21 type tomlEncodeError struct{ error }
     22 
     23 var (
     24 	errArrayNilElement = errors.New("toml: cannot encode array with nil element")
     25 	errNonString       = errors.New("toml: cannot encode a map with non-string key type")
     26 	errNoKey           = errors.New("toml: top-level values must be Go maps or structs")
     27 	errAnything        = errors.New("") // used in testing
     28 )
     29 
     30 var dblQuotedReplacer = strings.NewReplacer(
     31 	"\"", "\\\"",
     32 	"\\", "\\\\",
     33 	"\x00", `\u0000`,
     34 	"\x01", `\u0001`,
     35 	"\x02", `\u0002`,
     36 	"\x03", `\u0003`,
     37 	"\x04", `\u0004`,
     38 	"\x05", `\u0005`,
     39 	"\x06", `\u0006`,
     40 	"\x07", `\u0007`,
     41 	"\b", `\b`,
     42 	"\t", `\t`,
     43 	"\n", `\n`,
     44 	"\x0b", `\u000b`,
     45 	"\f", `\f`,
     46 	"\r", `\r`,
     47 	"\x0e", `\u000e`,
     48 	"\x0f", `\u000f`,
     49 	"\x10", `\u0010`,
     50 	"\x11", `\u0011`,
     51 	"\x12", `\u0012`,
     52 	"\x13", `\u0013`,
     53 	"\x14", `\u0014`,
     54 	"\x15", `\u0015`,
     55 	"\x16", `\u0016`,
     56 	"\x17", `\u0017`,
     57 	"\x18", `\u0018`,
     58 	"\x19", `\u0019`,
     59 	"\x1a", `\u001a`,
     60 	"\x1b", `\u001b`,
     61 	"\x1c", `\u001c`,
     62 	"\x1d", `\u001d`,
     63 	"\x1e", `\u001e`,
     64 	"\x1f", `\u001f`,
     65 	"\x7f", `\u007f`,
     66 )
     67 
     68 var (
     69 	marshalToml = reflect.TypeOf((*Marshaler)(nil)).Elem()
     70 	marshalText = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
     71 	timeType    = reflect.TypeOf((*time.Time)(nil)).Elem()
     72 )
     73 
     74 // Marshaler is the interface implemented by types that can marshal themselves
     75 // into valid TOML.
     76 type Marshaler interface {
     77 	MarshalTOML() ([]byte, error)
     78 }
     79 
     80 // Marshal returns a TOML representation of the Go value.
     81 //
     82 // See [Encoder] for a description of the encoding process.
     83 func Marshal(v any) ([]byte, error) {
     84 	buff := new(bytes.Buffer)
     85 	if err := NewEncoder(buff).Encode(v); err != nil {
     86 		return nil, err
     87 	}
     88 	return buff.Bytes(), nil
     89 }
     90 
     91 // Encoder encodes a Go to a TOML document.
     92 //
     93 // The mapping between Go values and TOML values should be precisely the same as
     94 // for [Decode].
     95 //
     96 // time.Time is encoded as a RFC 3339 string, and time.Duration as its string
     97 // representation.
     98 //
     99 // The [Marshaler] and [encoding.TextMarshaler] interfaces are supported to
    100 // encoding the value as custom TOML.
    101 //
    102 // If you want to write arbitrary binary data then you will need to use
    103 // something like base64 since TOML does not have any binary types.
    104 //
    105 // When encoding TOML hashes (Go maps or structs), keys without any sub-hashes
    106 // are encoded first.
    107 //
    108 // Go maps will be sorted alphabetically by key for deterministic output.
    109 //
    110 // The toml struct tag can be used to provide the key name; if omitted the
    111 // struct field name will be used. If the "omitempty" option is present the
    112 // following value will be skipped:
    113 //
    114 //   - arrays, slices, maps, and string with len of 0
    115 //   - struct with all zero values
    116 //   - bool false
    117 //
    118 // If omitzero is given all int and float types with a value of 0 will be
    119 // skipped.
    120 //
    121 // Encoding Go values without a corresponding TOML representation will return an
    122 // error. Examples of this includes maps with non-string keys, slices with nil
    123 // elements, embedded non-struct types, and nested slices containing maps or
    124 // structs. (e.g. [][]map[string]string is not allowed but []map[string]string
    125 // is okay, as is []map[string][]string).
    126 //
    127 // NOTE: only exported keys are encoded due to the use of reflection. Unexported
    128 // keys are silently discarded.
    129 type Encoder struct {
    130 	Indent     string // string for a single indentation level; default is two spaces.
    131 	hasWritten bool   // written any output to w yet?
    132 	w          *bufio.Writer
    133 }
    134 
    135 // NewEncoder create a new Encoder.
    136 func NewEncoder(w io.Writer) *Encoder {
    137 	return &Encoder{w: bufio.NewWriter(w), Indent: "  "}
    138 }
    139 
    140 // Encode writes a TOML representation of the Go value to the [Encoder]'s writer.
    141 //
    142 // An error is returned if the value given cannot be encoded to a valid TOML
    143 // document.
    144 func (enc *Encoder) Encode(v any) error {
    145 	rv := eindirect(reflect.ValueOf(v))
    146 	err := enc.safeEncode(Key([]string{}), rv)
    147 	if err != nil {
    148 		return err
    149 	}
    150 	return enc.w.Flush()
    151 }
    152 
    153 func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
    154 	defer func() {
    155 		if r := recover(); r != nil {
    156 			if terr, ok := r.(tomlEncodeError); ok {
    157 				err = terr.error
    158 				return
    159 			}
    160 			panic(r)
    161 		}
    162 	}()
    163 	enc.encode(key, rv)
    164 	return nil
    165 }
    166 
    167 func (enc *Encoder) encode(key Key, rv reflect.Value) {
    168 	// If we can marshal the type to text, then we use that. This prevents the
    169 	// encoder for handling these types as generic structs (or whatever the
    170 	// underlying type of a TextMarshaler is).
    171 	switch {
    172 	case isMarshaler(rv):
    173 		enc.writeKeyValue(key, rv, false)
    174 		return
    175 	case rv.Type() == primitiveType: // TODO: #76 would make this superfluous after implemented.
    176 		enc.encode(key, reflect.ValueOf(rv.Interface().(Primitive).undecoded))
    177 		return
    178 	}
    179 
    180 	k := rv.Kind()
    181 	switch k {
    182 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
    183 		reflect.Int64,
    184 		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
    185 		reflect.Uint64,
    186 		reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
    187 		enc.writeKeyValue(key, rv, false)
    188 	case reflect.Array, reflect.Slice:
    189 		if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
    190 			enc.eArrayOfTables(key, rv)
    191 		} else {
    192 			enc.writeKeyValue(key, rv, false)
    193 		}
    194 	case reflect.Interface:
    195 		if rv.IsNil() {
    196 			return
    197 		}
    198 		enc.encode(key, rv.Elem())
    199 	case reflect.Map:
    200 		if rv.IsNil() {
    201 			return
    202 		}
    203 		enc.eTable(key, rv)
    204 	case reflect.Ptr:
    205 		if rv.IsNil() {
    206 			return
    207 		}
    208 		enc.encode(key, rv.Elem())
    209 	case reflect.Struct:
    210 		enc.eTable(key, rv)
    211 	default:
    212 		encPanic(fmt.Errorf("unsupported type for key '%s': %s", key, k))
    213 	}
    214 }
    215 
    216 // eElement encodes any value that can be an array element.
    217 func (enc *Encoder) eElement(rv reflect.Value) {
    218 	switch v := rv.Interface().(type) {
    219 	case time.Time: // Using TextMarshaler adds extra quotes, which we don't want.
    220 		format := time.RFC3339Nano
    221 		switch v.Location() {
    222 		case internal.LocalDatetime:
    223 			format = "2006-01-02T15:04:05.999999999"
    224 		case internal.LocalDate:
    225 			format = "2006-01-02"
    226 		case internal.LocalTime:
    227 			format = "15:04:05.999999999"
    228 		}
    229 		switch v.Location() {
    230 		default:
    231 			enc.wf(v.Format(format))
    232 		case internal.LocalDatetime, internal.LocalDate, internal.LocalTime:
    233 			enc.wf(v.In(time.UTC).Format(format))
    234 		}
    235 		return
    236 	case Marshaler:
    237 		s, err := v.MarshalTOML()
    238 		if err != nil {
    239 			encPanic(err)
    240 		}
    241 		if s == nil {
    242 			encPanic(errors.New("MarshalTOML returned nil and no error"))
    243 		}
    244 		enc.w.Write(s)
    245 		return
    246 	case encoding.TextMarshaler:
    247 		s, err := v.MarshalText()
    248 		if err != nil {
    249 			encPanic(err)
    250 		}
    251 		if s == nil {
    252 			encPanic(errors.New("MarshalText returned nil and no error"))
    253 		}
    254 		enc.writeQuoted(string(s))
    255 		return
    256 	case time.Duration:
    257 		enc.writeQuoted(v.String())
    258 		return
    259 	case json.Number:
    260 		n, _ := rv.Interface().(json.Number)
    261 
    262 		if n == "" { /// Useful zero value.
    263 			enc.w.WriteByte('0')
    264 			return
    265 		} else if v, err := n.Int64(); err == nil {
    266 			enc.eElement(reflect.ValueOf(v))
    267 			return
    268 		} else if v, err := n.Float64(); err == nil {
    269 			enc.eElement(reflect.ValueOf(v))
    270 			return
    271 		}
    272 		encPanic(fmt.Errorf("unable to convert %q to int64 or float64", n))
    273 	}
    274 
    275 	switch rv.Kind() {
    276 	case reflect.Ptr:
    277 		enc.eElement(rv.Elem())
    278 		return
    279 	case reflect.String:
    280 		enc.writeQuoted(rv.String())
    281 	case reflect.Bool:
    282 		enc.wf(strconv.FormatBool(rv.Bool()))
    283 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    284 		enc.wf(strconv.FormatInt(rv.Int(), 10))
    285 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    286 		enc.wf(strconv.FormatUint(rv.Uint(), 10))
    287 	case reflect.Float32:
    288 		f := rv.Float()
    289 		if math.IsNaN(f) {
    290 			if math.Signbit(f) {
    291 				enc.wf("-")
    292 			}
    293 			enc.wf("nan")
    294 		} else if math.IsInf(f, 0) {
    295 			if math.Signbit(f) {
    296 				enc.wf("-")
    297 			}
    298 			enc.wf("inf")
    299 		} else {
    300 			enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 32)))
    301 		}
    302 	case reflect.Float64:
    303 		f := rv.Float()
    304 		if math.IsNaN(f) {
    305 			if math.Signbit(f) {
    306 				enc.wf("-")
    307 			}
    308 			enc.wf("nan")
    309 		} else if math.IsInf(f, 0) {
    310 			if math.Signbit(f) {
    311 				enc.wf("-")
    312 			}
    313 			enc.wf("inf")
    314 		} else {
    315 			enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 64)))
    316 		}
    317 	case reflect.Array, reflect.Slice:
    318 		enc.eArrayOrSliceElement(rv)
    319 	case reflect.Struct:
    320 		enc.eStruct(nil, rv, true)
    321 	case reflect.Map:
    322 		enc.eMap(nil, rv, true)
    323 	case reflect.Interface:
    324 		enc.eElement(rv.Elem())
    325 	default:
    326 		encPanic(fmt.Errorf("unexpected type: %s", fmtType(rv.Interface())))
    327 	}
    328 }
    329 
    330 // By the TOML spec, all floats must have a decimal with at least one number on
    331 // either side.
    332 func floatAddDecimal(fstr string) string {
    333 	if !strings.Contains(fstr, ".") {
    334 		return fstr + ".0"
    335 	}
    336 	return fstr
    337 }
    338 
    339 func (enc *Encoder) writeQuoted(s string) {
    340 	enc.wf("\"%s\"", dblQuotedReplacer.Replace(s))
    341 }
    342 
    343 func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
    344 	length := rv.Len()
    345 	enc.wf("[")
    346 	for i := 0; i < length; i++ {
    347 		elem := eindirect(rv.Index(i))
    348 		enc.eElement(elem)
    349 		if i != length-1 {
    350 			enc.wf(", ")
    351 		}
    352 	}
    353 	enc.wf("]")
    354 }
    355 
    356 func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
    357 	if len(key) == 0 {
    358 		encPanic(errNoKey)
    359 	}
    360 	for i := 0; i < rv.Len(); i++ {
    361 		trv := eindirect(rv.Index(i))
    362 		if isNil(trv) {
    363 			continue
    364 		}
    365 		enc.newline()
    366 		enc.wf("%s[[%s]]", enc.indentStr(key), key)
    367 		enc.newline()
    368 		enc.eMapOrStruct(key, trv, false)
    369 	}
    370 }
    371 
    372 func (enc *Encoder) eTable(key Key, rv reflect.Value) {
    373 	if len(key) == 1 {
    374 		// Output an extra newline between top-level tables.
    375 		// (The newline isn't written if nothing else has been written though.)
    376 		enc.newline()
    377 	}
    378 	if len(key) > 0 {
    379 		enc.wf("%s[%s]", enc.indentStr(key), key)
    380 		enc.newline()
    381 	}
    382 	enc.eMapOrStruct(key, rv, false)
    383 }
    384 
    385 func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value, inline bool) {
    386 	switch rv.Kind() {
    387 	case reflect.Map:
    388 		enc.eMap(key, rv, inline)
    389 	case reflect.Struct:
    390 		enc.eStruct(key, rv, inline)
    391 	default:
    392 		// Should never happen?
    393 		panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
    394 	}
    395 }
    396 
    397 func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
    398 	rt := rv.Type()
    399 	if rt.Key().Kind() != reflect.String {
    400 		encPanic(errNonString)
    401 	}
    402 
    403 	// Sort keys so that we have deterministic output. And write keys directly
    404 	// underneath this key first, before writing sub-structs or sub-maps.
    405 	var mapKeysDirect, mapKeysSub []string
    406 	for _, mapKey := range rv.MapKeys() {
    407 		k := mapKey.String()
    408 		if typeIsTable(tomlTypeOfGo(eindirect(rv.MapIndex(mapKey)))) {
    409 			mapKeysSub = append(mapKeysSub, k)
    410 		} else {
    411 			mapKeysDirect = append(mapKeysDirect, k)
    412 		}
    413 	}
    414 
    415 	var writeMapKeys = func(mapKeys []string, trailC bool) {
    416 		sort.Strings(mapKeys)
    417 		for i, mapKey := range mapKeys {
    418 			val := eindirect(rv.MapIndex(reflect.ValueOf(mapKey)))
    419 			if isNil(val) {
    420 				continue
    421 			}
    422 
    423 			if inline {
    424 				enc.writeKeyValue(Key{mapKey}, val, true)
    425 				if trailC || i != len(mapKeys)-1 {
    426 					enc.wf(", ")
    427 				}
    428 			} else {
    429 				enc.encode(key.add(mapKey), val)
    430 			}
    431 		}
    432 	}
    433 
    434 	if inline {
    435 		enc.wf("{")
    436 	}
    437 	writeMapKeys(mapKeysDirect, len(mapKeysSub) > 0)
    438 	writeMapKeys(mapKeysSub, false)
    439 	if inline {
    440 		enc.wf("}")
    441 	}
    442 }
    443 
    444 const is32Bit = (32 << (^uint(0) >> 63)) == 32
    445 
    446 func pointerTo(t reflect.Type) reflect.Type {
    447 	if t.Kind() == reflect.Ptr {
    448 		return pointerTo(t.Elem())
    449 	}
    450 	return t
    451 }
    452 
    453 func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
    454 	// Write keys for fields directly under this key first, because if we write
    455 	// a field that creates a new table then all keys under it will be in that
    456 	// table (not the one we're writing here).
    457 	//
    458 	// Fields is a [][]int: for fieldsDirect this always has one entry (the
    459 	// struct index). For fieldsSub it contains two entries: the parent field
    460 	// index from tv, and the field indexes for the fields of the sub.
    461 	var (
    462 		rt                      = rv.Type()
    463 		fieldsDirect, fieldsSub [][]int
    464 		addFields               func(rt reflect.Type, rv reflect.Value, start []int)
    465 	)
    466 	addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
    467 		for i := 0; i < rt.NumField(); i++ {
    468 			f := rt.Field(i)
    469 			isEmbed := f.Anonymous && pointerTo(f.Type).Kind() == reflect.Struct
    470 			if f.PkgPath != "" && !isEmbed { /// Skip unexported fields.
    471 				continue
    472 			}
    473 			opts := getOptions(f.Tag)
    474 			if opts.skip {
    475 				continue
    476 			}
    477 
    478 			frv := eindirect(rv.Field(i))
    479 
    480 			if is32Bit {
    481 				// Copy so it works correct on 32bit archs; not clear why this
    482 				// is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4
    483 				// This also works fine on 64bit, but 32bit archs are somewhat
    484 				// rare and this is a wee bit faster.
    485 				copyStart := make([]int, len(start))
    486 				copy(copyStart, start)
    487 				start = copyStart
    488 			}
    489 
    490 			// Treat anonymous struct fields with tag names as though they are
    491 			// not anonymous, like encoding/json does.
    492 			//
    493 			// Non-struct anonymous fields use the normal encoding logic.
    494 			if isEmbed {
    495 				if getOptions(f.Tag).name == "" && frv.Kind() == reflect.Struct {
    496 					addFields(frv.Type(), frv, append(start, f.Index...))
    497 					continue
    498 				}
    499 			}
    500 
    501 			if typeIsTable(tomlTypeOfGo(frv)) {
    502 				fieldsSub = append(fieldsSub, append(start, f.Index...))
    503 			} else {
    504 				fieldsDirect = append(fieldsDirect, append(start, f.Index...))
    505 			}
    506 		}
    507 	}
    508 	addFields(rt, rv, nil)
    509 
    510 	writeFields := func(fields [][]int) {
    511 		for _, fieldIndex := range fields {
    512 			fieldType := rt.FieldByIndex(fieldIndex)
    513 			fieldVal := rv.FieldByIndex(fieldIndex)
    514 
    515 			opts := getOptions(fieldType.Tag)
    516 			if opts.skip {
    517 				continue
    518 			}
    519 			if opts.omitempty && isEmpty(fieldVal) {
    520 				continue
    521 			}
    522 
    523 			fieldVal = eindirect(fieldVal)
    524 
    525 			if isNil(fieldVal) { /// Don't write anything for nil fields.
    526 				continue
    527 			}
    528 
    529 			keyName := fieldType.Name
    530 			if opts.name != "" {
    531 				keyName = opts.name
    532 			}
    533 
    534 			if opts.omitzero && isZero(fieldVal) {
    535 				continue
    536 			}
    537 
    538 			if inline {
    539 				enc.writeKeyValue(Key{keyName}, fieldVal, true)
    540 				if fieldIndex[0] != len(fields)-1 {
    541 					enc.wf(", ")
    542 				}
    543 			} else {
    544 				enc.encode(key.add(keyName), fieldVal)
    545 			}
    546 		}
    547 	}
    548 
    549 	if inline {
    550 		enc.wf("{")
    551 	}
    552 	writeFields(fieldsDirect)
    553 	writeFields(fieldsSub)
    554 	if inline {
    555 		enc.wf("}")
    556 	}
    557 }
    558 
    559 // tomlTypeOfGo returns the TOML type name of the Go value's type.
    560 //
    561 // It is used to determine whether the types of array elements are mixed (which
    562 // is forbidden). If the Go value is nil, then it is illegal for it to be an
    563 // array element, and valueIsNil is returned as true.
    564 //
    565 // The type may be `nil`, which means no concrete TOML type could be found.
    566 func tomlTypeOfGo(rv reflect.Value) tomlType {
    567 	if isNil(rv) || !rv.IsValid() {
    568 		return nil
    569 	}
    570 
    571 	if rv.Kind() == reflect.Struct {
    572 		if rv.Type() == timeType {
    573 			return tomlDatetime
    574 		}
    575 		if isMarshaler(rv) {
    576 			return tomlString
    577 		}
    578 		return tomlHash
    579 	}
    580 
    581 	if isMarshaler(rv) {
    582 		return tomlString
    583 	}
    584 
    585 	switch rv.Kind() {
    586 	case reflect.Bool:
    587 		return tomlBool
    588 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
    589 		reflect.Int64,
    590 		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
    591 		reflect.Uint64:
    592 		return tomlInteger
    593 	case reflect.Float32, reflect.Float64:
    594 		return tomlFloat
    595 	case reflect.Array, reflect.Slice:
    596 		if isTableArray(rv) {
    597 			return tomlArrayHash
    598 		}
    599 		return tomlArray
    600 	case reflect.Ptr, reflect.Interface:
    601 		return tomlTypeOfGo(rv.Elem())
    602 	case reflect.String:
    603 		return tomlString
    604 	case reflect.Map:
    605 		return tomlHash
    606 	default:
    607 		encPanic(errors.New("unsupported type: " + rv.Kind().String()))
    608 		panic("unreachable")
    609 	}
    610 }
    611 
    612 func isMarshaler(rv reflect.Value) bool {
    613 	return rv.Type().Implements(marshalText) || rv.Type().Implements(marshalToml)
    614 }
    615 
    616 // isTableArray reports if all entries in the array or slice are a table.
    617 func isTableArray(arr reflect.Value) bool {
    618 	if isNil(arr) || !arr.IsValid() || arr.Len() == 0 {
    619 		return false
    620 	}
    621 
    622 	ret := true
    623 	for i := 0; i < arr.Len(); i++ {
    624 		tt := tomlTypeOfGo(eindirect(arr.Index(i)))
    625 		// Don't allow nil.
    626 		if tt == nil {
    627 			encPanic(errArrayNilElement)
    628 		}
    629 
    630 		if ret && !typeEqual(tomlHash, tt) {
    631 			ret = false
    632 		}
    633 	}
    634 	return ret
    635 }
    636 
    637 type tagOptions struct {
    638 	skip      bool // "-"
    639 	name      string
    640 	omitempty bool
    641 	omitzero  bool
    642 }
    643 
    644 func getOptions(tag reflect.StructTag) tagOptions {
    645 	t := tag.Get("toml")
    646 	if t == "-" {
    647 		return tagOptions{skip: true}
    648 	}
    649 	var opts tagOptions
    650 	parts := strings.Split(t, ",")
    651 	opts.name = parts[0]
    652 	for _, s := range parts[1:] {
    653 		switch s {
    654 		case "omitempty":
    655 			opts.omitempty = true
    656 		case "omitzero":
    657 			opts.omitzero = true
    658 		}
    659 	}
    660 	return opts
    661 }
    662 
    663 func isZero(rv reflect.Value) bool {
    664 	switch rv.Kind() {
    665 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    666 		return rv.Int() == 0
    667 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    668 		return rv.Uint() == 0
    669 	case reflect.Float32, reflect.Float64:
    670 		return rv.Float() == 0.0
    671 	}
    672 	return false
    673 }
    674 
    675 func isEmpty(rv reflect.Value) bool {
    676 	switch rv.Kind() {
    677 	case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
    678 		return rv.Len() == 0
    679 	case reflect.Struct:
    680 		if rv.Type().Comparable() {
    681 			return reflect.Zero(rv.Type()).Interface() == rv.Interface()
    682 		}
    683 		// Need to also check if all the fields are empty, otherwise something
    684 		// like this with uncomparable types will always return true:
    685 		//
    686 		//   type a struct{ field b }
    687 		//   type b struct{ s []string }
    688 		//   s := a{field: b{s: []string{"AAA"}}}
    689 		for i := 0; i < rv.NumField(); i++ {
    690 			if !isEmpty(rv.Field(i)) {
    691 				return false
    692 			}
    693 		}
    694 		return true
    695 	case reflect.Bool:
    696 		return !rv.Bool()
    697 	case reflect.Ptr:
    698 		return rv.IsNil()
    699 	}
    700 	return false
    701 }
    702 
    703 func (enc *Encoder) newline() {
    704 	if enc.hasWritten {
    705 		enc.wf("\n")
    706 	}
    707 }
    708 
    709 // Write a key/value pair:
    710 //
    711 //	key = <any value>
    712 //
    713 // This is also used for "k = v" in inline tables; so something like this will
    714 // be written in three calls:
    715 //
    716 //	┌───────────────────┐
    717 //	│      ┌───┐  ┌────┐│
    718 //	v      v   v  v    vv
    719 //	key = {k = 1, k2 = 2}
    720 func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) {
    721 	/// Marshaler used on top-level document; call eElement() to just call
    722 	/// Marshal{TOML,Text}.
    723 	if len(key) == 0 {
    724 		enc.eElement(val)
    725 		return
    726 	}
    727 	enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
    728 	enc.eElement(val)
    729 	if !inline {
    730 		enc.newline()
    731 	}
    732 }
    733 
    734 func (enc *Encoder) wf(format string, v ...any) {
    735 	_, err := fmt.Fprintf(enc.w, format, v...)
    736 	if err != nil {
    737 		encPanic(err)
    738 	}
    739 	enc.hasWritten = true
    740 }
    741 
    742 func (enc *Encoder) indentStr(key Key) string {
    743 	return strings.Repeat(enc.Indent, len(key)-1)
    744 }
    745 
    746 func encPanic(err error) {
    747 	panic(tomlEncodeError{err})
    748 }
    749 
    750 // Resolve any level of pointers to the actual value (e.g. **string → string).
    751 func eindirect(v reflect.Value) reflect.Value {
    752 	if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
    753 		if isMarshaler(v) {
    754 			return v
    755 		}
    756 		if v.CanAddr() { /// Special case for marshalers; see #358.
    757 			if pv := v.Addr(); isMarshaler(pv) {
    758 				return pv
    759 			}
    760 		}
    761 		return v
    762 	}
    763 
    764 	if v.IsNil() {
    765 		return v
    766 	}
    767 
    768 	return eindirect(v.Elem())
    769 }
    770 
    771 func isNil(rv reflect.Value) bool {
    772 	switch rv.Kind() {
    773 	case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
    774 		return rv.IsNil()
    775 	default:
    776 		return false
    777 	}
    778 }