encode.go 17 KB

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