|
1 package toml |
|
2 |
|
3 import ( |
|
4 "bytes" |
|
5 "errors" |
|
6 "fmt" |
|
7 "io" |
|
8 "reflect" |
|
9 "strconv" |
|
10 "strings" |
|
11 "time" |
|
12 ) |
|
13 |
|
14 const tagKeyMultiline = "multiline" |
|
15 |
|
16 type tomlOpts struct { |
|
17 name string |
|
18 comment string |
|
19 commented bool |
|
20 multiline bool |
|
21 include bool |
|
22 omitempty bool |
|
23 } |
|
24 |
|
25 type encOpts struct { |
|
26 quoteMapKeys bool |
|
27 arraysOneElementPerLine bool |
|
28 } |
|
29 |
|
30 var encOptsDefaults = encOpts{ |
|
31 quoteMapKeys: false, |
|
32 } |
|
33 |
|
34 var timeType = reflect.TypeOf(time.Time{}) |
|
35 var marshalerType = reflect.TypeOf(new(Marshaler)).Elem() |
|
36 |
|
37 // Check if the given marshall type maps to a Tree primitive |
|
38 func 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 |
|
60 func 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 |
|
70 func 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 |
|
82 func 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 |
|
93 func isCustomMarshaler(mtype reflect.Type) bool { |
|
94 return mtype.Implements(marshalerType) |
|
95 } |
|
96 |
|
97 func 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. |
|
103 type Marshaler interface { |
|
104 MarshalTOML() ([]byte, error) |
|
105 } |
|
106 |
|
107 /* |
|
108 Marshal returns the TOML encoding of v. Behavior is similar to the Go json |
|
109 encoder, except that there is no concept of a Marshaler interface or MarshalTOML |
|
110 function for sub-structs, and currently only definite types can be marshaled |
|
111 (i.e. no `interface{}`). |
|
112 |
|
113 The 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 |
|
120 Note that pointers are automatically assigned the "omitempty" option, as TOML |
|
121 explicitly does not handle null values (saying instead the label should be |
|
122 dropped). |
|
123 |
|
124 Tree 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 |
|
131 Tree 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 */ |
|
140 func Marshal(v interface{}) ([]byte, error) { |
|
141 return NewEncoder(nil).marshal(v) |
|
142 } |
|
143 |
|
144 // Encoder writes TOML values to an output stream. |
|
145 type Encoder struct { |
|
146 w io.Writer |
|
147 encOpts |
|
148 } |
|
149 |
|
150 // NewEncoder returns a new encoder that writes to w. |
|
151 func 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. |
|
161 func (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. |
|
176 func (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 // ] |
|
195 func (e *Encoder) ArraysWithOneElementPerLine(v bool) *Encoder { |
|
196 e.arraysOneElementPerLine = v |
|
197 return e |
|
198 } |
|
199 |
|
200 func (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 |
|
221 func (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 |
|
266 func (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 |
|
279 func (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 |
|
292 func (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. |
|
328 func (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. |
|
335 func (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. |
|
352 func 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. |
|
361 type Decoder struct { |
|
362 r io.Reader |
|
363 tval *Tree |
|
364 encOpts |
|
365 } |
|
366 |
|
367 // NewDecoder returns a new decoder that reads from r. |
|
368 func 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. |
|
379 func (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 |
|
388 func (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 |
|
403 func (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 |
|
448 func (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 |
|
461 func (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 |
|
474 func (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 |
|
552 func (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 |
|
562 func 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 |
|
591 func 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 |
|
604 func 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 } |