Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / github.com / !burnt!sushi / toml@v0.3.1 / encode.go
1 package toml
2
3 import (
4         "bufio"
5         "errors"
6         "fmt"
7         "io"
8         "reflect"
9         "sort"
10         "strconv"
11         "strings"
12         "time"
13 )
14
15 type tomlEncodeError struct{ error }
16
17 var (
18         errArrayMixedElementTypes = errors.New(
19                 "toml: cannot encode array with mixed element types")
20         errArrayNilElement = errors.New(
21                 "toml: cannot encode array with nil element")
22         errNonString = errors.New(
23                 "toml: cannot encode a map with non-string key type")
24         errAnonNonStruct = errors.New(
25                 "toml: cannot encode an anonymous field that is not a struct")
26         errArrayNoTable = errors.New(
27                 "toml: TOML array element cannot contain a table")
28         errNoKey = errors.New(
29                 "toml: top-level values must be Go maps or structs")
30         errAnything = errors.New("") // used in testing
31 )
32
33 var quotedReplacer = strings.NewReplacer(
34         "\t", "\\t",
35         "\n", "\\n",
36         "\r", "\\r",
37         "\"", "\\\"",
38         "\\", "\\\\",
39 )
40
41 // Encoder controls the encoding of Go values to a TOML document to some
42 // io.Writer.
43 //
44 // The indentation level can be controlled with the Indent field.
45 type Encoder struct {
46         // A single indentation level. By default it is two spaces.
47         Indent string
48
49         // hasWritten is whether we have written any output to w yet.
50         hasWritten bool
51         w          *bufio.Writer
52 }
53
54 // NewEncoder returns a TOML encoder that encodes Go values to the io.Writer
55 // given. By default, a single indentation level is 2 spaces.
56 func NewEncoder(w io.Writer) *Encoder {
57         return &Encoder{
58                 w:      bufio.NewWriter(w),
59                 Indent: "  ",
60         }
61 }
62
63 // Encode writes a TOML representation of the Go value to the underlying
64 // io.Writer. If the value given cannot be encoded to a valid TOML document,
65 // then an error is returned.
66 //
67 // The mapping between Go values and TOML values should be precisely the same
68 // as for the Decode* functions. Similarly, the TextMarshaler interface is
69 // supported by encoding the resulting bytes as strings. (If you want to write
70 // arbitrary binary data then you will need to use something like base64 since
71 // TOML does not have any binary types.)
72 //
73 // When encoding TOML hashes (i.e., Go maps or structs), keys without any
74 // sub-hashes are encoded first.
75 //
76 // If a Go map is encoded, then its keys are sorted alphabetically for
77 // deterministic output. More control over this behavior may be provided if
78 // there is demand for it.
79 //
80 // Encoding Go values without a corresponding TOML representation---like map
81 // types with non-string keys---will cause an error to be returned. Similarly
82 // for mixed arrays/slices, arrays/slices with nil elements, embedded
83 // non-struct types and nested slices containing maps or structs.
84 // (e.g., [][]map[string]string is not allowed but []map[string]string is OK
85 // and so is []map[string][]string.)
86 func (enc *Encoder) Encode(v interface{}) error {
87         rv := eindirect(reflect.ValueOf(v))
88         if err := enc.safeEncode(Key([]string{}), rv); err != nil {
89                 return err
90         }
91         return enc.w.Flush()
92 }
93
94 func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
95         defer func() {
96                 if r := recover(); r != nil {
97                         if terr, ok := r.(tomlEncodeError); ok {
98                                 err = terr.error
99                                 return
100                         }
101                         panic(r)
102                 }
103         }()
104         enc.encode(key, rv)
105         return nil
106 }
107
108 func (enc *Encoder) encode(key Key, rv reflect.Value) {
109         // Special case. Time needs to be in ISO8601 format.
110         // Special case. If we can marshal the type to text, then we used that.
111         // Basically, this prevents the encoder for handling these types as
112         // generic structs (or whatever the underlying type of a TextMarshaler is).
113         switch rv.Interface().(type) {
114         case time.Time, TextMarshaler:
115                 enc.keyEqElement(key, rv)
116                 return
117         }
118
119         k := rv.Kind()
120         switch k {
121         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
122                 reflect.Int64,
123                 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
124                 reflect.Uint64,
125                 reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
126                 enc.keyEqElement(key, rv)
127         case reflect.Array, reflect.Slice:
128                 if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
129                         enc.eArrayOfTables(key, rv)
130                 } else {
131                         enc.keyEqElement(key, rv)
132                 }
133         case reflect.Interface:
134                 if rv.IsNil() {
135                         return
136                 }
137                 enc.encode(key, rv.Elem())
138         case reflect.Map:
139                 if rv.IsNil() {
140                         return
141                 }
142                 enc.eTable(key, rv)
143         case reflect.Ptr:
144                 if rv.IsNil() {
145                         return
146                 }
147                 enc.encode(key, rv.Elem())
148         case reflect.Struct:
149                 enc.eTable(key, rv)
150         default:
151                 panic(e("unsupported type for key '%s': %s", key, k))
152         }
153 }
154
155 // eElement encodes any value that can be an array element (primitives and
156 // arrays).
157 func (enc *Encoder) eElement(rv reflect.Value) {
158         switch v := rv.Interface().(type) {
159         case time.Time:
160                 // Special case time.Time as a primitive. Has to come before
161                 // TextMarshaler below because time.Time implements
162                 // encoding.TextMarshaler, but we need to always use UTC.
163                 enc.wf(v.UTC().Format("2006-01-02T15:04:05Z"))
164                 return
165         case TextMarshaler:
166                 // Special case. Use text marshaler if it's available for this value.
167                 if s, err := v.MarshalText(); err != nil {
168                         encPanic(err)
169                 } else {
170                         enc.writeQuoted(string(s))
171                 }
172                 return
173         }
174         switch rv.Kind() {
175         case reflect.Bool:
176                 enc.wf(strconv.FormatBool(rv.Bool()))
177         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
178                 reflect.Int64:
179                 enc.wf(strconv.FormatInt(rv.Int(), 10))
180         case reflect.Uint, reflect.Uint8, reflect.Uint16,
181                 reflect.Uint32, reflect.Uint64:
182                 enc.wf(strconv.FormatUint(rv.Uint(), 10))
183         case reflect.Float32:
184                 enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32)))
185         case reflect.Float64:
186                 enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64)))
187         case reflect.Array, reflect.Slice:
188                 enc.eArrayOrSliceElement(rv)
189         case reflect.Interface:
190                 enc.eElement(rv.Elem())
191         case reflect.String:
192                 enc.writeQuoted(rv.String())
193         default:
194                 panic(e("unexpected primitive type: %s", rv.Kind()))
195         }
196 }
197
198 // By the TOML spec, all floats must have a decimal with at least one
199 // number on either side.
200 func floatAddDecimal(fstr string) string {
201         if !strings.Contains(fstr, ".") {
202                 return fstr + ".0"
203         }
204         return fstr
205 }
206
207 func (enc *Encoder) writeQuoted(s string) {
208         enc.wf("\"%s\"", quotedReplacer.Replace(s))
209 }
210
211 func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
212         length := rv.Len()
213         enc.wf("[")
214         for i := 0; i < length; i++ {
215                 elem := rv.Index(i)
216                 enc.eElement(elem)
217                 if i != length-1 {
218                         enc.wf(", ")
219                 }
220         }
221         enc.wf("]")
222 }
223
224 func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
225         if len(key) == 0 {
226                 encPanic(errNoKey)
227         }
228         for i := 0; i < rv.Len(); i++ {
229                 trv := rv.Index(i)
230                 if isNil(trv) {
231                         continue
232                 }
233                 panicIfInvalidKey(key)
234                 enc.newline()
235                 enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll())
236                 enc.newline()
237                 enc.eMapOrStruct(key, trv)
238         }
239 }
240
241 func (enc *Encoder) eTable(key Key, rv reflect.Value) {
242         panicIfInvalidKey(key)
243         if len(key) == 1 {
244                 // Output an extra newline between top-level tables.
245                 // (The newline isn't written if nothing else has been written though.)
246                 enc.newline()
247         }
248         if len(key) > 0 {
249                 enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll())
250                 enc.newline()
251         }
252         enc.eMapOrStruct(key, rv)
253 }
254
255 func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) {
256         switch rv := eindirect(rv); rv.Kind() {
257         case reflect.Map:
258                 enc.eMap(key, rv)
259         case reflect.Struct:
260                 enc.eStruct(key, rv)
261         default:
262                 panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
263         }
264 }
265
266 func (enc *Encoder) eMap(key Key, rv reflect.Value) {
267         rt := rv.Type()
268         if rt.Key().Kind() != reflect.String {
269                 encPanic(errNonString)
270         }
271
272         // Sort keys so that we have deterministic output. And write keys directly
273         // underneath this key first, before writing sub-structs or sub-maps.
274         var mapKeysDirect, mapKeysSub []string
275         for _, mapKey := range rv.MapKeys() {
276                 k := mapKey.String()
277                 if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) {
278                         mapKeysSub = append(mapKeysSub, k)
279                 } else {
280                         mapKeysDirect = append(mapKeysDirect, k)
281                 }
282         }
283
284         var writeMapKeys = func(mapKeys []string) {
285                 sort.Strings(mapKeys)
286                 for _, mapKey := range mapKeys {
287                         mrv := rv.MapIndex(reflect.ValueOf(mapKey))
288                         if isNil(mrv) {
289                                 // Don't write anything for nil fields.
290                                 continue
291                         }
292                         enc.encode(key.add(mapKey), mrv)
293                 }
294         }
295         writeMapKeys(mapKeysDirect)
296         writeMapKeys(mapKeysSub)
297 }
298
299 func (enc *Encoder) eStruct(key Key, rv reflect.Value) {
300         // Write keys for fields directly under this key first, because if we write
301         // a field that creates a new table, then all keys under it will be in that
302         // table (not the one we're writing here).
303         rt := rv.Type()
304         var fieldsDirect, fieldsSub [][]int
305         var addFields func(rt reflect.Type, rv reflect.Value, start []int)
306         addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
307                 for i := 0; i < rt.NumField(); i++ {
308                         f := rt.Field(i)
309                         // skip unexported fields
310                         if f.PkgPath != "" && !f.Anonymous {
311                                 continue
312                         }
313                         frv := rv.Field(i)
314                         if f.Anonymous {
315                                 t := f.Type
316                                 switch t.Kind() {
317                                 case reflect.Struct:
318                                         // Treat anonymous struct fields with
319                                         // tag names as though they are not
320                                         // anonymous, like encoding/json does.
321                                         if getOptions(f.Tag).name == "" {
322                                                 addFields(t, frv, f.Index)
323                                                 continue
324                                         }
325                                 case reflect.Ptr:
326                                         if t.Elem().Kind() == reflect.Struct &&
327                                                 getOptions(f.Tag).name == "" {
328                                                 if !frv.IsNil() {
329                                                         addFields(t.Elem(), frv.Elem(), f.Index)
330                                                 }
331                                                 continue
332                                         }
333                                         // Fall through to the normal field encoding logic below
334                                         // for non-struct anonymous fields.
335                                 }
336                         }
337
338                         if typeIsHash(tomlTypeOfGo(frv)) {
339                                 fieldsSub = append(fieldsSub, append(start, f.Index...))
340                         } else {
341                                 fieldsDirect = append(fieldsDirect, append(start, f.Index...))
342                         }
343                 }
344         }
345         addFields(rt, rv, nil)
346
347         var writeFields = func(fields [][]int) {
348                 for _, fieldIndex := range fields {
349                         sft := rt.FieldByIndex(fieldIndex)
350                         sf := rv.FieldByIndex(fieldIndex)
351                         if isNil(sf) {
352                                 // Don't write anything for nil fields.
353                                 continue
354                         }
355
356                         opts := getOptions(sft.Tag)
357                         if opts.skip {
358                                 continue
359                         }
360                         keyName := sft.Name
361                         if opts.name != "" {
362                                 keyName = opts.name
363                         }
364                         if opts.omitempty && isEmpty(sf) {
365                                 continue
366                         }
367                         if opts.omitzero && isZero(sf) {
368                                 continue
369                         }
370
371                         enc.encode(key.add(keyName), sf)
372                 }
373         }
374         writeFields(fieldsDirect)
375         writeFields(fieldsSub)
376 }
377
378 // tomlTypeName returns the TOML type name of the Go value's type. It is
379 // used to determine whether the types of array elements are mixed (which is
380 // forbidden). If the Go value is nil, then it is illegal for it to be an array
381 // element, and valueIsNil is returned as true.
382
383 // Returns the TOML type of a Go value. The type may be `nil`, which means
384 // no concrete TOML type could be found.
385 func tomlTypeOfGo(rv reflect.Value) tomlType {
386         if isNil(rv) || !rv.IsValid() {
387                 return nil
388         }
389         switch rv.Kind() {
390         case reflect.Bool:
391                 return tomlBool
392         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
393                 reflect.Int64,
394                 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
395                 reflect.Uint64:
396                 return tomlInteger
397         case reflect.Float32, reflect.Float64:
398                 return tomlFloat
399         case reflect.Array, reflect.Slice:
400                 if typeEqual(tomlHash, tomlArrayType(rv)) {
401                         return tomlArrayHash
402                 }
403                 return tomlArray
404         case reflect.Ptr, reflect.Interface:
405                 return tomlTypeOfGo(rv.Elem())
406         case reflect.String:
407                 return tomlString
408         case reflect.Map:
409                 return tomlHash
410         case reflect.Struct:
411                 switch rv.Interface().(type) {
412                 case time.Time:
413                         return tomlDatetime
414                 case TextMarshaler:
415                         return tomlString
416                 default:
417                         return tomlHash
418                 }
419         default:
420                 panic("unexpected reflect.Kind: " + rv.Kind().String())
421         }
422 }
423
424 // tomlArrayType returns the element type of a TOML array. The type returned
425 // may be nil if it cannot be determined (e.g., a nil slice or a zero length
426 // slize). This function may also panic if it finds a type that cannot be
427 // expressed in TOML (such as nil elements, heterogeneous arrays or directly
428 // nested arrays of tables).
429 func tomlArrayType(rv reflect.Value) tomlType {
430         if isNil(rv) || !rv.IsValid() || rv.Len() == 0 {
431                 return nil
432         }
433         firstType := tomlTypeOfGo(rv.Index(0))
434         if firstType == nil {
435                 encPanic(errArrayNilElement)
436         }
437
438         rvlen := rv.Len()
439         for i := 1; i < rvlen; i++ {
440                 elem := rv.Index(i)
441                 switch elemType := tomlTypeOfGo(elem); {
442                 case elemType == nil:
443                         encPanic(errArrayNilElement)
444                 case !typeEqual(firstType, elemType):
445                         encPanic(errArrayMixedElementTypes)
446                 }
447         }
448         // If we have a nested array, then we must make sure that the nested
449         // array contains ONLY primitives.
450         // This checks arbitrarily nested arrays.
451         if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) {
452                 nest := tomlArrayType(eindirect(rv.Index(0)))
453                 if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) {
454                         encPanic(errArrayNoTable)
455                 }
456         }
457         return firstType
458 }
459
460 type tagOptions struct {
461         skip      bool // "-"
462         name      string
463         omitempty bool
464         omitzero  bool
465 }
466
467 func getOptions(tag reflect.StructTag) tagOptions {
468         t := tag.Get("toml")
469         if t == "-" {
470                 return tagOptions{skip: true}
471         }
472         var opts tagOptions
473         parts := strings.Split(t, ",")
474         opts.name = parts[0]
475         for _, s := range parts[1:] {
476                 switch s {
477                 case "omitempty":
478                         opts.omitempty = true
479                 case "omitzero":
480                         opts.omitzero = true
481                 }
482         }
483         return opts
484 }
485
486 func isZero(rv reflect.Value) bool {
487         switch rv.Kind() {
488         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
489                 return rv.Int() == 0
490         case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
491                 return rv.Uint() == 0
492         case reflect.Float32, reflect.Float64:
493                 return rv.Float() == 0.0
494         }
495         return false
496 }
497
498 func isEmpty(rv reflect.Value) bool {
499         switch rv.Kind() {
500         case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
501                 return rv.Len() == 0
502         case reflect.Bool:
503                 return !rv.Bool()
504         }
505         return false
506 }
507
508 func (enc *Encoder) newline() {
509         if enc.hasWritten {
510                 enc.wf("\n")
511         }
512 }
513
514 func (enc *Encoder) keyEqElement(key Key, val reflect.Value) {
515         if len(key) == 0 {
516                 encPanic(errNoKey)
517         }
518         panicIfInvalidKey(key)
519         enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
520         enc.eElement(val)
521         enc.newline()
522 }
523
524 func (enc *Encoder) wf(format string, v ...interface{}) {
525         if _, err := fmt.Fprintf(enc.w, format, v...); err != nil {
526                 encPanic(err)
527         }
528         enc.hasWritten = true
529 }
530
531 func (enc *Encoder) indentStr(key Key) string {
532         return strings.Repeat(enc.Indent, len(key)-1)
533 }
534
535 func encPanic(err error) {
536         panic(tomlEncodeError{err})
537 }
538
539 func eindirect(v reflect.Value) reflect.Value {
540         switch v.Kind() {
541         case reflect.Ptr, reflect.Interface:
542                 return eindirect(v.Elem())
543         default:
544                 return v
545         }
546 }
547
548 func isNil(rv reflect.Value) bool {
549         switch rv.Kind() {
550         case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
551                 return rv.IsNil()
552         default:
553                 return false
554         }
555 }
556
557 func panicIfInvalidKey(key Key) {
558         for _, k := range key {
559                 if len(k) == 0 {
560                         encPanic(e("Key '%s' is not a valid table name. Key names "+
561                                 "cannot be empty.", key.maybeQuotedAll()))
562                 }
563         }
564 }
565
566 func isValidKeyName(s string) bool {
567         return len(s) != 0
568 }