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