1package toml
2
3import (
4	"bytes"
5	"errors"
6	"fmt"
7	"io"
8	"reflect"
9	"strconv"
10	"strings"
11	"time"
12)
13
14const tagKeyMultiline = "multiline"
15
16type tomlOpts struct {
17	name      string
18	comment   string
19	commented bool
20	multiline bool
21	include   bool
22	omitempty bool
23}
24
25type encOpts struct {
26	quoteMapKeys            bool
27	arraysOneElementPerLine bool
28}
29
30var encOptsDefaults = encOpts{
31	quoteMapKeys: false,
32}
33
34var timeType = reflect.TypeOf(time.Time{})
35var marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
36
37// Check if the given marshall type maps to a Tree primitive
38func isPrimitive(mtype reflect.Type) bool {
39	switch mtype.Kind() {
40	case reflect.Ptr:
41		return isPrimitive(mtype.Elem())
42	case reflect.Bool:
43		return true
44	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
45		return true
46	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
47		return true
48	case reflect.Float32, reflect.Float64:
49		return true
50	case reflect.String:
51		return true
52	case reflect.Struct:
53		return mtype == timeType || isCustomMarshaler(mtype)
54	default:
55		return false
56	}
57}
58
59// Check if the given marshall type maps to a Tree slice
60func isTreeSlice(mtype reflect.Type) bool {
61	switch mtype.Kind() {
62	case reflect.Slice:
63		return !isOtherSlice(mtype)
64	default:
65		return false
66	}
67}
68
69// Check if the given marshall type maps to a non-Tree slice
70func isOtherSlice(mtype reflect.Type) bool {
71	switch mtype.Kind() {
72	case reflect.Ptr:
73		return isOtherSlice(mtype.Elem())
74	case reflect.Slice:
75		return isPrimitive(mtype.Elem()) || isOtherSlice(mtype.Elem())
76	default:
77		return false
78	}
79}
80
81// Check if the given marshall type maps to a Tree
82func isTree(mtype reflect.Type) bool {
83	switch mtype.Kind() {
84	case reflect.Map:
85		return true
86	case reflect.Struct:
87		return !isPrimitive(mtype)
88	default:
89		return false
90	}
91}
92
93func isCustomMarshaler(mtype reflect.Type) bool {
94	return mtype.Implements(marshalerType)
95}
96
97func callCustomMarshaler(mval reflect.Value) ([]byte, error) {
98	return mval.Interface().(Marshaler).MarshalTOML()
99}
100
101// Marshaler is the interface implemented by types that
102// can marshal themselves into valid TOML.
103type Marshaler interface {
104	MarshalTOML() ([]byte, error)
105}
106
107/*
108Marshal returns the TOML encoding of v.  Behavior is similar to the Go json
109encoder, except that there is no concept of a Marshaler interface or MarshalTOML
110function for sub-structs, and currently only definite types can be marshaled
111(i.e. no `interface{}`).
112
113The following struct annotations are supported:
114
115  toml:"Field"      Overrides the field's name to output.
116  omitempty         When set, empty values and groups are not emitted.
117  comment:"comment" Emits a # comment on the same line. This supports new lines.
118  commented:"true"  Emits the value as commented.
119
120Note that pointers are automatically assigned the "omitempty" option, as TOML
121explicitly does not handle null values (saying instead the label should be
122dropped).
123
124Tree structural types and corresponding marshal types:
125
126  *Tree                            (*)struct, (*)map[string]interface{}
127  []*Tree                          (*)[](*)struct, (*)[](*)map[string]interface{}
128  []interface{} (as interface{})   (*)[]primitive, (*)[]([]interface{})
129  interface{}                      (*)primitive
130
131Tree primitive types and corresponding marshal types:
132
133  uint64     uint, uint8-uint64, pointers to same
134  int64      int, int8-uint64, pointers to same
135  float64    float32, float64, pointers to same
136  string     string, pointers to same
137  bool       bool, pointers to same
138  time.Time  time.Time{}, pointers to same
139*/
140func Marshal(v interface{}) ([]byte, error) {
141	return NewEncoder(nil).marshal(v)
142}
143
144// Encoder writes TOML values to an output stream.
145type Encoder struct {
146	w io.Writer
147	encOpts
148}
149
150// NewEncoder returns a new encoder that writes to w.
151func NewEncoder(w io.Writer) *Encoder {
152	return &Encoder{
153		w:       w,
154		encOpts: encOptsDefaults,
155	}
156}
157
158// Encode writes the TOML encoding of v to the stream.
159//
160// See the documentation for Marshal for details.
161func (e *Encoder) Encode(v interface{}) error {
162	b, err := e.marshal(v)
163	if err != nil {
164		return err
165	}
166	if _, err := e.w.Write(b); err != nil {
167		return err
168	}
169	return nil
170}
171
172// QuoteMapKeys sets up the encoder to encode
173// maps with string type keys with quoted TOML keys.
174//
175// This relieves the character limitations on map keys.
176func (e *Encoder) QuoteMapKeys(v bool) *Encoder {
177	e.quoteMapKeys = v
178	return e
179}
180
181// ArraysWithOneElementPerLine sets up the encoder to encode arrays
182// with more than one element on multiple lines instead of one.
183//
184// For example:
185//
186//   A = [1,2,3]
187//
188// Becomes
189//
190//   A = [
191//     1,
192//     2,
193//     3,
194//   ]
195func (e *Encoder) ArraysWithOneElementPerLine(v bool) *Encoder {
196	e.arraysOneElementPerLine = v
197	return e
198}
199
200func (e *Encoder) marshal(v interface{}) ([]byte, error) {
201	mtype := reflect.TypeOf(v)
202	if mtype.Kind() != reflect.Struct {
203		return []byte{}, errors.New("Only a struct can be marshaled to TOML")
204	}
205	sval := reflect.ValueOf(v)
206	if isCustomMarshaler(mtype) {
207		return callCustomMarshaler(sval)
208	}
209	t, err := e.valueToTree(mtype, sval)
210	if err != nil {
211		return []byte{}, err
212	}
213
214	var buf bytes.Buffer
215	_, err = t.writeTo(&buf, "", "", 0, e.arraysOneElementPerLine)
216
217	return buf.Bytes(), err
218}
219
220// Convert given marshal struct or map value to toml tree
221func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) {
222	if mtype.Kind() == reflect.Ptr {
223		return e.valueToTree(mtype.Elem(), mval.Elem())
224	}
225	tval := newTree()
226	switch mtype.Kind() {
227	case reflect.Struct:
228		for i := 0; i < mtype.NumField(); i++ {
229			mtypef, mvalf := mtype.Field(i), mval.Field(i)
230			opts := tomlOptions(mtypef)
231			if opts.include && (!opts.omitempty || !isZero(mvalf)) {
232				val, err := e.valueToToml(mtypef.Type, mvalf)
233				if err != nil {
234					return nil, err
235				}
236
237				tval.SetWithOptions(opts.name, SetOptions{
238					Comment:   opts.comment,
239					Commented: opts.commented,
240					Multiline: opts.multiline,
241				}, val)
242			}
243		}
244	case reflect.Map:
245		for _, key := range mval.MapKeys() {
246			mvalf := mval.MapIndex(key)
247			val, err := e.valueToToml(mtype.Elem(), mvalf)
248			if err != nil {
249				return nil, err
250			}
251			if e.quoteMapKeys {
252				keyStr, err := tomlValueStringRepresentation(key.String(), "", e.arraysOneElementPerLine)
253				if err != nil {
254					return nil, err
255				}
256				tval.SetPath([]string{keyStr}, val)
257			} else {
258				tval.Set(key.String(), val)
259			}
260		}
261	}
262	return tval, nil
263}
264
265// Convert given marshal slice to slice of Toml trees
266func (e *Encoder) valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) {
267	tval := make([]*Tree, mval.Len(), mval.Len())
268	for i := 0; i < mval.Len(); i++ {
269		val, err := e.valueToTree(mtype.Elem(), mval.Index(i))
270		if err != nil {
271			return nil, err
272		}
273		tval[i] = val
274	}
275	return tval, nil
276}
277
278// Convert given marshal slice to slice of toml values
279func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
280	tval := make([]interface{}, mval.Len(), mval.Len())
281	for i := 0; i < mval.Len(); i++ {
282		val, err := e.valueToToml(mtype.Elem(), mval.Index(i))
283		if err != nil {
284			return nil, err
285		}
286		tval[i] = val
287	}
288	return tval, nil
289}
290
291// Convert given marshal value to toml value
292func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
293	if mtype.Kind() == reflect.Ptr {
294		return e.valueToToml(mtype.Elem(), mval.Elem())
295	}
296	switch {
297	case isCustomMarshaler(mtype):
298		return callCustomMarshaler(mval)
299	case isTree(mtype):
300		return e.valueToTree(mtype, mval)
301	case isTreeSlice(mtype):
302		return e.valueToTreeSlice(mtype, mval)
303	case isOtherSlice(mtype):
304		return e.valueToOtherSlice(mtype, mval)
305	default:
306		switch mtype.Kind() {
307		case reflect.Bool:
308			return mval.Bool(), nil
309		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
310			return mval.Int(), nil
311		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
312			return mval.Uint(), nil
313		case reflect.Float32, reflect.Float64:
314			return mval.Float(), nil
315		case reflect.String:
316			return mval.String(), nil
317		case reflect.Struct:
318			return mval.Interface().(time.Time), nil
319		default:
320			return nil, fmt.Errorf("Marshal can't handle %v(%v)", mtype, mtype.Kind())
321		}
322	}
323}
324
325// Unmarshal attempts to unmarshal the Tree into a Go struct pointed by v.
326// Neither Unmarshaler interfaces nor UnmarshalTOML functions are supported for
327// sub-structs, and only definite types can be unmarshaled.
328func (t *Tree) Unmarshal(v interface{}) error {
329	d := Decoder{tval: t}
330	return d.unmarshal(v)
331}
332
333// Marshal returns the TOML encoding of Tree.
334// See Marshal() documentation for types mapping table.
335func (t *Tree) Marshal() ([]byte, error) {
336	var buf bytes.Buffer
337	err := NewEncoder(&buf).Encode(t)
338	return buf.Bytes(), err
339}
340
341// Unmarshal parses the TOML-encoded data and stores the result in the value
342// pointed to by v. Behavior is similar to the Go json encoder, except that there
343// is no concept of an Unmarshaler interface or UnmarshalTOML function for
344// sub-structs, and currently only definite types can be unmarshaled to (i.e. no
345// `interface{}`).
346//
347// The following struct annotations are supported:
348//
349//   toml:"Field" Overrides the field's name to map to.
350//
351// See Marshal() documentation for types mapping table.
352func Unmarshal(data []byte, v interface{}) error {
353	t, err := LoadReader(bytes.NewReader(data))
354	if err != nil {
355		return err
356	}
357	return t.Unmarshal(v)
358}
359
360// Decoder reads and decodes TOML values from an input stream.
361type Decoder struct {
362	r    io.Reader
363	tval *Tree
364	encOpts
365}
366
367// NewDecoder returns a new decoder that reads from r.
368func NewDecoder(r io.Reader) *Decoder {
369	return &Decoder{
370		r:       r,
371		encOpts: encOptsDefaults,
372	}
373}
374
375// Decode reads a TOML-encoded value from it's input
376// and unmarshals it in the value pointed at by v.
377//
378// See the documentation for Marshal for details.
379func (d *Decoder) Decode(v interface{}) error {
380	var err error
381	d.tval, err = LoadReader(d.r)
382	if err != nil {
383		return err
384	}
385	return d.unmarshal(v)
386}
387
388func (d *Decoder) unmarshal(v interface{}) error {
389	mtype := reflect.TypeOf(v)
390	if mtype.Kind() != reflect.Ptr || mtype.Elem().Kind() != reflect.Struct {
391		return errors.New("Only a pointer to struct can be unmarshaled from TOML")
392	}
393
394	sval, err := d.valueFromTree(mtype.Elem(), d.tval)
395	if err != nil {
396		return err
397	}
398	reflect.ValueOf(v).Elem().Set(sval)
399	return nil
400}
401
402// Convert toml tree to marshal struct or map, using marshal type
403func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) {
404	if mtype.Kind() == reflect.Ptr {
405		return d.unwrapPointer(mtype, tval)
406	}
407	var mval reflect.Value
408	switch mtype.Kind() {
409	case reflect.Struct:
410		mval = reflect.New(mtype).Elem()
411		for i := 0; i < mtype.NumField(); i++ {
412			mtypef := mtype.Field(i)
413			opts := tomlOptions(mtypef)
414			if opts.include {
415				baseKey := opts.name
416				keysToTry := []string{baseKey, strings.ToLower(baseKey), strings.ToTitle(baseKey)}
417				for _, key := range keysToTry {
418					exists := tval.Has(key)
419					if !exists {
420						continue
421					}
422					val := tval.Get(key)
423					mvalf, err := d.valueFromToml(mtypef.Type, val)
424					if err != nil {
425						return mval, formatError(err, tval.GetPosition(key))
426					}
427					mval.Field(i).Set(mvalf)
428					break
429				}
430			}
431		}
432	case reflect.Map:
433		mval = reflect.MakeMap(mtype)
434		for _, key := range tval.Keys() {
435			// TODO: path splits key
436			val := tval.GetPath([]string{key})
437			mvalf, err := d.valueFromToml(mtype.Elem(), val)
438			if err != nil {
439				return mval, formatError(err, tval.GetPosition(key))
440			}
441			mval.SetMapIndex(reflect.ValueOf(key), mvalf)
442		}
443	}
444	return mval, nil
445}
446
447// Convert toml value to marshal struct/map slice, using marshal type
448func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) {
449	mval := reflect.MakeSlice(mtype, len(tval), len(tval))
450	for i := 0; i < len(tval); i++ {
451		val, err := d.valueFromTree(mtype.Elem(), tval[i])
452		if err != nil {
453			return mval, err
454		}
455		mval.Index(i).Set(val)
456	}
457	return mval, nil
458}
459
460// Convert toml value to marshal primitive slice, using marshal type
461func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) {
462	mval := reflect.MakeSlice(mtype, len(tval), len(tval))
463	for i := 0; i < len(tval); i++ {
464		val, err := d.valueFromToml(mtype.Elem(), tval[i])
465		if err != nil {
466			return mval, err
467		}
468		mval.Index(i).Set(val)
469	}
470	return mval, nil
471}
472
473// Convert toml value to marshal value, using marshal type
474func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
475	if mtype.Kind() == reflect.Ptr {
476		return d.unwrapPointer(mtype, tval)
477	}
478
479	switch tval.(type) {
480	case *Tree:
481		if isTree(mtype) {
482			return d.valueFromTree(mtype, tval.(*Tree))
483		}
484		return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval)
485	case []*Tree:
486		if isTreeSlice(mtype) {
487			return d.valueFromTreeSlice(mtype, tval.([]*Tree))
488		}
489		return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval)
490	case []interface{}:
491		if isOtherSlice(mtype) {
492			return d.valueFromOtherSlice(mtype, tval.([]interface{}))
493		}
494		return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval)
495	default:
496		switch mtype.Kind() {
497		case reflect.Bool, reflect.Struct:
498			val := reflect.ValueOf(tval)
499			// if this passes for when mtype is reflect.Struct, tval is a time.Time
500			if !val.Type().ConvertibleTo(mtype) {
501				return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
502			}
503
504			return val.Convert(mtype), nil
505		case reflect.String:
506			val := reflect.ValueOf(tval)
507			// stupidly, int64 is convertible to string. So special case this.
508			if !val.Type().ConvertibleTo(mtype) || val.Kind() == reflect.Int64 {
509				return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
510			}
511
512			return val.Convert(mtype), nil
513		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
514			val := reflect.ValueOf(tval)
515			if !val.Type().ConvertibleTo(mtype) {
516				return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
517			}
518			if reflect.Indirect(reflect.New(mtype)).OverflowInt(val.Int()) {
519				return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String())
520			}
521
522			return val.Convert(mtype), nil
523		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
524			val := reflect.ValueOf(tval)
525			if !val.Type().ConvertibleTo(mtype) {
526				return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
527			}
528			if val.Int() < 0 {
529				return reflect.ValueOf(nil), fmt.Errorf("%v(%T) is negative so does not fit in %v", tval, tval, mtype.String())
530			}
531			if reflect.Indirect(reflect.New(mtype)).OverflowUint(uint64(val.Int())) {
532				return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String())
533			}
534
535			return val.Convert(mtype), nil
536		case reflect.Float32, reflect.Float64:
537			val := reflect.ValueOf(tval)
538			if !val.Type().ConvertibleTo(mtype) {
539				return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
540			}
541			if reflect.Indirect(reflect.New(mtype)).OverflowFloat(val.Float()) {
542				return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String())
543			}
544
545			return val.Convert(mtype), nil
546		default:
547			return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v(%v)", tval, tval, mtype, mtype.Kind())
548		}
549	}
550}
551
552func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
553	val, err := d.valueFromToml(mtype.Elem(), tval)
554	if err != nil {
555		return reflect.ValueOf(nil), err
556	}
557	mval := reflect.New(mtype.Elem())
558	mval.Elem().Set(val)
559	return mval, nil
560}
561
562func tomlOptions(vf reflect.StructField) tomlOpts {
563	tag := vf.Tag.Get("toml")
564	parse := strings.Split(tag, ",")
565	var comment string
566	if c := vf.Tag.Get("comment"); c != "" {
567		comment = c
568	}
569	commented, _ := strconv.ParseBool(vf.Tag.Get("commented"))
570	multiline, _ := strconv.ParseBool(vf.Tag.Get(tagKeyMultiline))
571	result := tomlOpts{name: vf.Name, comment: comment, commented: commented, multiline: multiline, include: true, omitempty: false}
572	if parse[0] != "" {
573		if parse[0] == "-" && len(parse) == 1 {
574			result.include = false
575		} else {
576			result.name = strings.Trim(parse[0], " ")
577		}
578	}
579	if vf.PkgPath != "" {
580		result.include = false
581	}
582	if len(parse) > 1 && strings.Trim(parse[1], " ") == "omitempty" {
583		result.omitempty = true
584	}
585	if vf.Type.Kind() == reflect.Ptr {
586		result.omitempty = true
587	}
588	return result
589}
590
591func isZero(val reflect.Value) bool {
592	switch val.Type().Kind() {
593	case reflect.Map:
594		fallthrough
595	case reflect.Array:
596		fallthrough
597	case reflect.Slice:
598		return val.Len() == 0
599	default:
600		return reflect.DeepEqual(val.Interface(), reflect.Zero(val.Type()).Interface())
601	}
602}
603
604func formatError(err error, pos Position) error {
605	if err.Error()[0] == '(' { // Error already contains position information
606		return err
607	}
608	return fmt.Errorf("%s: %s", pos, err)
609}
610