1package toml 2 3import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "reflect" 9 "sort" 10 "strconv" 11 "strings" 12 "time" 13) 14 15const ( 16 tagFieldName = "toml" 17 tagFieldComment = "comment" 18 tagCommented = "commented" 19 tagMultiline = "multiline" 20 tagDefault = "default" 21) 22 23type tomlOpts struct { 24 name string 25 comment string 26 commented bool 27 multiline bool 28 include bool 29 omitempty bool 30 defaultValue string 31} 32 33type encOpts struct { 34 quoteMapKeys bool 35 arraysOneElementPerLine bool 36} 37 38var encOptsDefaults = encOpts{ 39 quoteMapKeys: false, 40} 41 42type annotation struct { 43 tag string 44 comment string 45 commented string 46 multiline string 47 defaultValue string 48} 49 50var annotationDefault = annotation{ 51 tag: tagFieldName, 52 comment: tagFieldComment, 53 commented: tagCommented, 54 multiline: tagMultiline, 55 defaultValue: tagDefault, 56} 57 58type marshalOrder int 59 60// Orders the Encoder can write the fields to the output stream. 61const ( 62 // Sort fields alphabetically. 63 OrderAlphabetical marshalOrder = iota + 1 64 // Preserve the order the fields are encountered. For example, the order of fields in 65 // a struct. 66 OrderPreserve 67) 68 69var timeType = reflect.TypeOf(time.Time{}) 70var marshalerType = reflect.TypeOf(new(Marshaler)).Elem() 71 72// Check if the given marshal type maps to a Tree primitive 73func isPrimitive(mtype reflect.Type) bool { 74 switch mtype.Kind() { 75 case reflect.Ptr: 76 return isPrimitive(mtype.Elem()) 77 case reflect.Bool: 78 return true 79 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 80 return true 81 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 82 return true 83 case reflect.Float32, reflect.Float64: 84 return true 85 case reflect.String: 86 return true 87 case reflect.Struct: 88 return mtype == timeType || isCustomMarshaler(mtype) 89 default: 90 return false 91 } 92} 93 94// Check if the given marshal type maps to a Tree slice 95func isTreeSlice(mtype reflect.Type) bool { 96 switch mtype.Kind() { 97 case reflect.Slice: 98 return !isOtherSlice(mtype) 99 default: 100 return false 101 } 102} 103 104// Check if the given marshal type maps to a non-Tree slice 105func isOtherSlice(mtype reflect.Type) bool { 106 switch mtype.Kind() { 107 case reflect.Ptr: 108 return isOtherSlice(mtype.Elem()) 109 case reflect.Slice: 110 return isPrimitive(mtype.Elem()) || isOtherSlice(mtype.Elem()) 111 default: 112 return false 113 } 114} 115 116// Check if the given marshal type maps to a Tree 117func isTree(mtype reflect.Type) bool { 118 switch mtype.Kind() { 119 case reflect.Map: 120 return true 121 case reflect.Struct: 122 return !isPrimitive(mtype) 123 default: 124 return false 125 } 126} 127 128func isCustomMarshaler(mtype reflect.Type) bool { 129 return mtype.Implements(marshalerType) 130} 131 132func callCustomMarshaler(mval reflect.Value) ([]byte, error) { 133 return mval.Interface().(Marshaler).MarshalTOML() 134} 135 136// Marshaler is the interface implemented by types that 137// can marshal themselves into valid TOML. 138type Marshaler interface { 139 MarshalTOML() ([]byte, error) 140} 141 142/* 143Marshal returns the TOML encoding of v. Behavior is similar to the Go json 144encoder, except that there is no concept of a Marshaler interface or MarshalTOML 145function for sub-structs, and currently only definite types can be marshaled 146(i.e. no `interface{}`). 147 148The following struct annotations are supported: 149 150 toml:"Field" Overrides the field's name to output. 151 omitempty When set, empty values and groups are not emitted. 152 comment:"comment" Emits a # comment on the same line. This supports new lines. 153 commented:"true" Emits the value as commented. 154 155Note that pointers are automatically assigned the "omitempty" option, as TOML 156explicitly does not handle null values (saying instead the label should be 157dropped). 158 159Tree structural types and corresponding marshal types: 160 161 *Tree (*)struct, (*)map[string]interface{} 162 []*Tree (*)[](*)struct, (*)[](*)map[string]interface{} 163 []interface{} (as interface{}) (*)[]primitive, (*)[]([]interface{}) 164 interface{} (*)primitive 165 166Tree primitive types and corresponding marshal types: 167 168 uint64 uint, uint8-uint64, pointers to same 169 int64 int, int8-uint64, pointers to same 170 float64 float32, float64, pointers to same 171 string string, pointers to same 172 bool bool, pointers to same 173 time.Time time.Time{}, pointers to same 174 175For additional flexibility, use the Encoder API. 176*/ 177func Marshal(v interface{}) ([]byte, error) { 178 return NewEncoder(nil).marshal(v) 179} 180 181// Encoder writes TOML values to an output stream. 182type Encoder struct { 183 w io.Writer 184 encOpts 185 annotation 186 line int 187 col int 188 order marshalOrder 189} 190 191// NewEncoder returns a new encoder that writes to w. 192func NewEncoder(w io.Writer) *Encoder { 193 return &Encoder{ 194 w: w, 195 encOpts: encOptsDefaults, 196 annotation: annotationDefault, 197 line: 0, 198 col: 1, 199 order: OrderAlphabetical, 200 } 201} 202 203// Encode writes the TOML encoding of v to the stream. 204// 205// See the documentation for Marshal for details. 206func (e *Encoder) Encode(v interface{}) error { 207 b, err := e.marshal(v) 208 if err != nil { 209 return err 210 } 211 if _, err := e.w.Write(b); err != nil { 212 return err 213 } 214 return nil 215} 216 217// QuoteMapKeys sets up the encoder to encode 218// maps with string type keys with quoted TOML keys. 219// 220// This relieves the character limitations on map keys. 221func (e *Encoder) QuoteMapKeys(v bool) *Encoder { 222 e.quoteMapKeys = v 223 return e 224} 225 226// ArraysWithOneElementPerLine sets up the encoder to encode arrays 227// with more than one element on multiple lines instead of one. 228// 229// For example: 230// 231// A = [1,2,3] 232// 233// Becomes 234// 235// A = [ 236// 1, 237// 2, 238// 3, 239// ] 240func (e *Encoder) ArraysWithOneElementPerLine(v bool) *Encoder { 241 e.arraysOneElementPerLine = v 242 return e 243} 244 245// Order allows to change in which order fields will be written to the output stream. 246func (e *Encoder) Order(ord marshalOrder) *Encoder { 247 e.order = ord 248 return e 249} 250 251// SetTagName allows changing default tag "toml" 252func (e *Encoder) SetTagName(v string) *Encoder { 253 e.tag = v 254 return e 255} 256 257// SetTagComment allows changing default tag "comment" 258func (e *Encoder) SetTagComment(v string) *Encoder { 259 e.comment = v 260 return e 261} 262 263// SetTagCommented allows changing default tag "commented" 264func (e *Encoder) SetTagCommented(v string) *Encoder { 265 e.commented = v 266 return e 267} 268 269// SetTagMultiline allows changing default tag "multiline" 270func (e *Encoder) SetTagMultiline(v string) *Encoder { 271 e.multiline = v 272 return e 273} 274 275func (e *Encoder) marshal(v interface{}) ([]byte, error) { 276 mtype := reflect.TypeOf(v) 277 278 switch mtype.Kind() { 279 case reflect.Struct, reflect.Map: 280 case reflect.Ptr: 281 if mtype.Elem().Kind() != reflect.Struct { 282 return []byte{}, errors.New("Only pointer to struct can be marshaled to TOML") 283 } 284 default: 285 return []byte{}, errors.New("Only a struct or map can be marshaled to TOML") 286 } 287 288 sval := reflect.ValueOf(v) 289 if isCustomMarshaler(mtype) { 290 return callCustomMarshaler(sval) 291 } 292 t, err := e.valueToTree(mtype, sval) 293 if err != nil { 294 return []byte{}, err 295 } 296 297 var buf bytes.Buffer 298 _, err = t.writeToOrdered(&buf, "", "", 0, e.arraysOneElementPerLine, e.order) 299 300 return buf.Bytes(), err 301} 302 303// Create next tree with a position based on Encoder.line 304func (e *Encoder) nextTree() *Tree { 305 return newTreeWithPosition(Position{Line: e.line, Col: 1}) 306} 307 308// Convert given marshal struct or map value to toml tree 309func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) { 310 if mtype.Kind() == reflect.Ptr { 311 return e.valueToTree(mtype.Elem(), mval.Elem()) 312 } 313 tval := e.nextTree() 314 switch mtype.Kind() { 315 case reflect.Struct: 316 for i := 0; i < mtype.NumField(); i++ { 317 mtypef, mvalf := mtype.Field(i), mval.Field(i) 318 opts := tomlOptions(mtypef, e.annotation) 319 if opts.include && (!opts.omitempty || !isZero(mvalf)) { 320 val, err := e.valueToToml(mtypef.Type, mvalf) 321 if err != nil { 322 return nil, err 323 } 324 325 tval.SetWithOptions(opts.name, SetOptions{ 326 Comment: opts.comment, 327 Commented: opts.commented, 328 Multiline: opts.multiline, 329 }, val) 330 } 331 } 332 case reflect.Map: 333 keys := mval.MapKeys() 334 if e.order == OrderPreserve && len(keys) > 0 { 335 // Sorting []reflect.Value is not straight forward. 336 // 337 // OrderPreserve will support deterministic results when string is used 338 // as the key to maps. 339 typ := keys[0].Type() 340 kind := keys[0].Kind() 341 if kind == reflect.String { 342 ikeys := make([]string, len(keys)) 343 for i := range keys { 344 ikeys[i] = keys[i].Interface().(string) 345 } 346 sort.Strings(ikeys) 347 for i := range ikeys { 348 keys[i] = reflect.ValueOf(ikeys[i]).Convert(typ) 349 } 350 } 351 } 352 for _, key := range keys { 353 mvalf := mval.MapIndex(key) 354 val, err := e.valueToToml(mtype.Elem(), mvalf) 355 if err != nil { 356 return nil, err 357 } 358 if e.quoteMapKeys { 359 keyStr, err := tomlValueStringRepresentation(key.String(), "", e.arraysOneElementPerLine) 360 if err != nil { 361 return nil, err 362 } 363 tval.SetPath([]string{keyStr}, val) 364 } else { 365 tval.Set(key.String(), val) 366 } 367 } 368 } 369 return tval, nil 370} 371 372// Convert given marshal slice to slice of Toml trees 373func (e *Encoder) valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) { 374 tval := make([]*Tree, mval.Len(), mval.Len()) 375 for i := 0; i < mval.Len(); i++ { 376 val, err := e.valueToTree(mtype.Elem(), mval.Index(i)) 377 if err != nil { 378 return nil, err 379 } 380 tval[i] = val 381 } 382 return tval, nil 383} 384 385// Convert given marshal slice to slice of toml values 386func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) { 387 tval := make([]interface{}, mval.Len(), mval.Len()) 388 for i := 0; i < mval.Len(); i++ { 389 val, err := e.valueToToml(mtype.Elem(), mval.Index(i)) 390 if err != nil { 391 return nil, err 392 } 393 tval[i] = val 394 } 395 return tval, nil 396} 397 398// Convert given marshal value to toml value 399func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) { 400 e.line++ 401 if mtype.Kind() == reflect.Ptr { 402 return e.valueToToml(mtype.Elem(), mval.Elem()) 403 } 404 switch { 405 case isCustomMarshaler(mtype): 406 return callCustomMarshaler(mval) 407 case isTree(mtype): 408 return e.valueToTree(mtype, mval) 409 case isTreeSlice(mtype): 410 return e.valueToTreeSlice(mtype, mval) 411 case isOtherSlice(mtype): 412 return e.valueToOtherSlice(mtype, mval) 413 default: 414 switch mtype.Kind() { 415 case reflect.Bool: 416 return mval.Bool(), nil 417 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 418 if mtype.Kind() == reflect.Int64 && mtype == reflect.TypeOf(time.Duration(1)) { 419 return fmt.Sprint(mval), nil 420 } 421 return mval.Int(), nil 422 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 423 return mval.Uint(), nil 424 case reflect.Float32, reflect.Float64: 425 return mval.Float(), nil 426 case reflect.String: 427 return mval.String(), nil 428 case reflect.Struct: 429 return mval.Interface().(time.Time), nil 430 default: 431 return nil, fmt.Errorf("Marshal can't handle %v(%v)", mtype, mtype.Kind()) 432 } 433 } 434} 435 436// Unmarshal attempts to unmarshal the Tree into a Go struct pointed by v. 437// Neither Unmarshaler interfaces nor UnmarshalTOML functions are supported for 438// sub-structs, and only definite types can be unmarshaled. 439func (t *Tree) Unmarshal(v interface{}) error { 440 d := Decoder{tval: t, tagName: tagFieldName} 441 return d.unmarshal(v) 442} 443 444// Marshal returns the TOML encoding of Tree. 445// See Marshal() documentation for types mapping table. 446func (t *Tree) Marshal() ([]byte, error) { 447 var buf bytes.Buffer 448 err := NewEncoder(&buf).Encode(t) 449 return buf.Bytes(), err 450} 451 452// Unmarshal parses the TOML-encoded data and stores the result in the value 453// pointed to by v. Behavior is similar to the Go json encoder, except that there 454// is no concept of an Unmarshaler interface or UnmarshalTOML function for 455// sub-structs, and currently only definite types can be unmarshaled to (i.e. no 456// `interface{}`). 457// 458// The following struct annotations are supported: 459// 460// toml:"Field" Overrides the field's name to map to. 461// default:"foo" Provides a default value. 462// 463// For default values, only fields of the following types are supported: 464// * string 465// * bool 466// * int 467// * int64 468// * float64 469// 470// See Marshal() documentation for types mapping table. 471func Unmarshal(data []byte, v interface{}) error { 472 t, err := LoadReader(bytes.NewReader(data)) 473 if err != nil { 474 return err 475 } 476 return t.Unmarshal(v) 477} 478 479// Decoder reads and decodes TOML values from an input stream. 480type Decoder struct { 481 r io.Reader 482 tval *Tree 483 encOpts 484 tagName string 485} 486 487// NewDecoder returns a new decoder that reads from r. 488func NewDecoder(r io.Reader) *Decoder { 489 return &Decoder{ 490 r: r, 491 encOpts: encOptsDefaults, 492 tagName: tagFieldName, 493 } 494} 495 496// Decode reads a TOML-encoded value from it's input 497// and unmarshals it in the value pointed at by v. 498// 499// See the documentation for Marshal for details. 500func (d *Decoder) Decode(v interface{}) error { 501 var err error 502 d.tval, err = LoadReader(d.r) 503 if err != nil { 504 return err 505 } 506 return d.unmarshal(v) 507} 508 509// SetTagName allows changing default tag "toml" 510func (d *Decoder) SetTagName(v string) *Decoder { 511 d.tagName = v 512 return d 513} 514 515func (d *Decoder) unmarshal(v interface{}) error { 516 mtype := reflect.TypeOf(v) 517 if mtype.Kind() != reflect.Ptr { 518 return errors.New("only a pointer to struct or map can be unmarshaled from TOML") 519 } 520 521 elem := mtype.Elem() 522 523 switch elem.Kind() { 524 case reflect.Struct, reflect.Map: 525 default: 526 return errors.New("only a pointer to struct or map can be unmarshaled from TOML") 527 } 528 529 sval, err := d.valueFromTree(elem, d.tval) 530 if err != nil { 531 return err 532 } 533 reflect.ValueOf(v).Elem().Set(sval) 534 return nil 535} 536 537// Convert toml tree to marshal struct or map, using marshal type 538func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) { 539 if mtype.Kind() == reflect.Ptr { 540 return d.unwrapPointer(mtype, tval) 541 } 542 var mval reflect.Value 543 switch mtype.Kind() { 544 case reflect.Struct: 545 mval = reflect.New(mtype).Elem() 546 for i := 0; i < mtype.NumField(); i++ { 547 mtypef := mtype.Field(i) 548 an := annotation{tag: d.tagName} 549 opts := tomlOptions(mtypef, an) 550 if opts.include { 551 baseKey := opts.name 552 keysToTry := []string{ 553 baseKey, 554 strings.ToLower(baseKey), 555 strings.ToTitle(baseKey), 556 strings.ToLower(string(baseKey[0])) + baseKey[1:], 557 } 558 559 found := false 560 for _, key := range keysToTry { 561 exists := tval.Has(key) 562 if !exists { 563 continue 564 } 565 val := tval.Get(key) 566 mvalf, err := d.valueFromToml(mtypef.Type, val) 567 if err != nil { 568 return mval, formatError(err, tval.GetPosition(key)) 569 } 570 mval.Field(i).Set(mvalf) 571 found = true 572 break 573 } 574 575 if !found && opts.defaultValue != "" { 576 mvalf := mval.Field(i) 577 var val interface{} 578 var err error 579 switch mvalf.Kind() { 580 case reflect.Bool: 581 val, err = strconv.ParseBool(opts.defaultValue) 582 if err != nil { 583 return mval.Field(i), err 584 } 585 case reflect.Int: 586 val, err = strconv.Atoi(opts.defaultValue) 587 if err != nil { 588 return mval.Field(i), err 589 } 590 case reflect.String: 591 val = opts.defaultValue 592 case reflect.Int64: 593 val, err = strconv.ParseInt(opts.defaultValue, 10, 64) 594 if err != nil { 595 return mval.Field(i), err 596 } 597 case reflect.Float64: 598 val, err = strconv.ParseFloat(opts.defaultValue, 64) 599 if err != nil { 600 return mval.Field(i), err 601 } 602 default: 603 return mval.Field(i), fmt.Errorf("unsuported field type for default option") 604 } 605 mval.Field(i).Set(reflect.ValueOf(val)) 606 } 607 } 608 } 609 case reflect.Map: 610 mval = reflect.MakeMap(mtype) 611 for _, key := range tval.Keys() { 612 // TODO: path splits key 613 val := tval.GetPath([]string{key}) 614 mvalf, err := d.valueFromToml(mtype.Elem(), val) 615 if err != nil { 616 return mval, formatError(err, tval.GetPosition(key)) 617 } 618 mval.SetMapIndex(reflect.ValueOf(key).Convert(mtype.Key()), mvalf) 619 } 620 } 621 return mval, nil 622} 623 624// Convert toml value to marshal struct/map slice, using marshal type 625func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) { 626 mval := reflect.MakeSlice(mtype, len(tval), len(tval)) 627 for i := 0; i < len(tval); i++ { 628 val, err := d.valueFromTree(mtype.Elem(), tval[i]) 629 if err != nil { 630 return mval, err 631 } 632 mval.Index(i).Set(val) 633 } 634 return mval, nil 635} 636 637// Convert toml value to marshal primitive slice, using marshal type 638func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) { 639 mval := reflect.MakeSlice(mtype, len(tval), len(tval)) 640 for i := 0; i < len(tval); i++ { 641 val, err := d.valueFromToml(mtype.Elem(), tval[i]) 642 if err != nil { 643 return mval, err 644 } 645 mval.Index(i).Set(val) 646 } 647 return mval, nil 648} 649 650// Convert toml value to marshal value, using marshal type 651func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) { 652 if mtype.Kind() == reflect.Ptr { 653 return d.unwrapPointer(mtype, tval) 654 } 655 656 switch t := tval.(type) { 657 case *Tree: 658 if isTree(mtype) { 659 return d.valueFromTree(mtype, t) 660 } 661 return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval) 662 case []*Tree: 663 if isTreeSlice(mtype) { 664 return d.valueFromTreeSlice(mtype, t) 665 } 666 return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval) 667 case []interface{}: 668 if isOtherSlice(mtype) { 669 return d.valueFromOtherSlice(mtype, t) 670 } 671 return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval) 672 default: 673 switch mtype.Kind() { 674 case reflect.Bool, reflect.Struct: 675 val := reflect.ValueOf(tval) 676 // if this passes for when mtype is reflect.Struct, tval is a time.Time 677 if !val.Type().ConvertibleTo(mtype) { 678 return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) 679 } 680 681 return val.Convert(mtype), nil 682 case reflect.String: 683 val := reflect.ValueOf(tval) 684 // stupidly, int64 is convertible to string. So special case this. 685 if !val.Type().ConvertibleTo(mtype) || val.Kind() == reflect.Int64 { 686 return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) 687 } 688 689 return val.Convert(mtype), nil 690 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 691 val := reflect.ValueOf(tval) 692 if mtype.Kind() == reflect.Int64 && mtype == reflect.TypeOf(time.Duration(1)) && val.Kind() == reflect.String { 693 d, err := time.ParseDuration(val.String()) 694 if err != nil { 695 return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v. %s", tval, tval, mtype.String(), err) 696 } 697 return reflect.ValueOf(d), nil 698 } 699 if !val.Type().ConvertibleTo(mtype) { 700 return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) 701 } 702 if reflect.Indirect(reflect.New(mtype)).OverflowInt(val.Convert(mtype).Int()) { 703 return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String()) 704 } 705 706 return val.Convert(mtype), nil 707 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 708 val := reflect.ValueOf(tval) 709 if !val.Type().ConvertibleTo(mtype) { 710 return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) 711 } 712 713 if val.Convert(reflect.TypeOf(int(1))).Int() < 0 { 714 return reflect.ValueOf(nil), fmt.Errorf("%v(%T) is negative so does not fit in %v", tval, tval, mtype.String()) 715 } 716 if reflect.Indirect(reflect.New(mtype)).OverflowUint(uint64(val.Convert(mtype).Uint())) { 717 return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String()) 718 } 719 720 return val.Convert(mtype), nil 721 case reflect.Float32, reflect.Float64: 722 val := reflect.ValueOf(tval) 723 if !val.Type().ConvertibleTo(mtype) { 724 return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) 725 } 726 if reflect.Indirect(reflect.New(mtype)).OverflowFloat(val.Convert(mtype).Float()) { 727 return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String()) 728 } 729 730 return val.Convert(mtype), nil 731 default: 732 return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v(%v)", tval, tval, mtype, mtype.Kind()) 733 } 734 } 735} 736 737func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) { 738 val, err := d.valueFromToml(mtype.Elem(), tval) 739 if err != nil { 740 return reflect.ValueOf(nil), err 741 } 742 mval := reflect.New(mtype.Elem()) 743 mval.Elem().Set(val) 744 return mval, nil 745} 746 747func tomlOptions(vf reflect.StructField, an annotation) tomlOpts { 748 tag := vf.Tag.Get(an.tag) 749 parse := strings.Split(tag, ",") 750 var comment string 751 if c := vf.Tag.Get(an.comment); c != "" { 752 comment = c 753 } 754 commented, _ := strconv.ParseBool(vf.Tag.Get(an.commented)) 755 multiline, _ := strconv.ParseBool(vf.Tag.Get(an.multiline)) 756 defaultValue := vf.Tag.Get(tagDefault) 757 result := tomlOpts{ 758 name: vf.Name, 759 comment: comment, 760 commented: commented, 761 multiline: multiline, 762 include: true, 763 omitempty: false, 764 defaultValue: defaultValue, 765 } 766 if parse[0] != "" { 767 if parse[0] == "-" && len(parse) == 1 { 768 result.include = false 769 } else { 770 result.name = strings.Trim(parse[0], " ") 771 } 772 } 773 if vf.PkgPath != "" { 774 result.include = false 775 } 776 if len(parse) > 1 && strings.Trim(parse[1], " ") == "omitempty" { 777 result.omitempty = true 778 } 779 if vf.Type.Kind() == reflect.Ptr { 780 result.omitempty = true 781 } 782 return result 783} 784 785func isZero(val reflect.Value) bool { 786 switch val.Type().Kind() { 787 case reflect.Map: 788 fallthrough 789 case reflect.Array: 790 fallthrough 791 case reflect.Slice: 792 return val.Len() == 0 793 default: 794 return reflect.DeepEqual(val.Interface(), reflect.Zero(val.Type()).Interface()) 795 } 796} 797 798func formatError(err error, pos Position) error { 799 if err.Error()[0] == '(' { // Error already contains position information 800 return err 801 } 802 return fmt.Errorf("%s: %s", pos, err) 803} 804