github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_codec.go (about) 1 // Copyright (C) MongoDB, Inc. 2017-present. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7 package bsoncodec 8 9 import ( 10 "errors" 11 "fmt" 12 "reflect" 13 "sort" 14 "strings" 15 "sync" 16 "time" 17 18 "go.mongodb.org/mongo-driver/bson/bsonoptions" 19 "go.mongodb.org/mongo-driver/bson/bsonrw" 20 "go.mongodb.org/mongo-driver/bson/bsontype" 21 ) 22 23 // DecodeError represents an error that occurs when unmarshalling BSON bytes into a native Go type. 24 type DecodeError struct { 25 keys []string 26 wrapped error 27 } 28 29 // Unwrap returns the underlying error 30 func (de *DecodeError) Unwrap() error { 31 return de.wrapped 32 } 33 34 // Error implements the error interface. 35 func (de *DecodeError) Error() string { 36 // The keys are stored in reverse order because the de.keys slice is builtup while propagating the error up the 37 // stack of BSON keys, so we call de.Keys(), which reverses them. 38 keyPath := strings.Join(de.Keys(), ".") 39 return fmt.Sprintf("error decoding key %s: %v", keyPath, de.wrapped) 40 } 41 42 // Keys returns the BSON key path that caused an error as a slice of strings. The keys in the slice are in top-down 43 // order. For example, if the document being unmarshalled was {a: {b: {c: 1}}} and the value for c was supposed to be 44 // a string, the keys slice will be ["a", "b", "c"]. 45 func (de *DecodeError) Keys() []string { 46 reversedKeys := make([]string, 0, len(de.keys)) 47 for idx := len(de.keys) - 1; idx >= 0; idx-- { 48 reversedKeys = append(reversedKeys, de.keys[idx]) 49 } 50 51 return reversedKeys 52 } 53 54 // Zeroer allows custom struct types to implement a report of zero 55 // state. All struct types that don't implement Zeroer or where IsZero 56 // returns false are considered to be not zero. 57 type Zeroer interface { 58 IsZero() bool 59 } 60 61 // StructCodec is the Codec used for struct values. 62 // 63 // Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with the 64 // StructCodec registered. 65 type StructCodec struct { 66 cache map[reflect.Type]*structDescription 67 l sync.RWMutex 68 parser StructTagParser 69 70 // DecodeZeroStruct causes DecodeValue to delete any existing values from Go structs in the 71 // destination value passed to Decode before unmarshaling BSON documents into them. 72 // 73 // Deprecated: Use bson.Decoder.ZeroStructs instead. 74 DecodeZeroStruct bool 75 76 // DecodeDeepZeroInline causes DecodeValue to delete any existing values from Go structs in the 77 // destination value passed to Decode before unmarshaling BSON documents into them. 78 // 79 // Deprecated: DecodeDeepZeroInline will not be supported in Go Driver 2.0. 80 DecodeDeepZeroInline bool 81 82 // EncodeOmitDefaultStruct causes the Encoder to consider the zero value for a struct (e.g. 83 // MyStruct{}) as empty and omit it from the marshaled BSON when the "omitempty" struct tag 84 // option is set. 85 // 86 // Deprecated: Use bson.Encoder.OmitZeroStruct instead. 87 EncodeOmitDefaultStruct bool 88 89 // AllowUnexportedFields allows encoding and decoding values from un-exported struct fields. 90 // 91 // Deprecated: AllowUnexportedFields does not work on recent versions of Go and will not be 92 // supported in Go Driver 2.0. 93 AllowUnexportedFields bool 94 95 // OverwriteDuplicatedInlinedFields, if false, causes EncodeValue to return an error if there is 96 // a duplicate field in the marshaled BSON when the "inline" struct tag option is set. The 97 // default value is true. 98 // 99 // Deprecated: Use bson.Encoder.ErrorOnInlineDuplicates instead. 100 OverwriteDuplicatedInlinedFields bool 101 } 102 103 var _ ValueEncoder = &StructCodec{} 104 var _ ValueDecoder = &StructCodec{} 105 106 // NewStructCodec returns a StructCodec that uses p for struct tag parsing. 107 // 108 // Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with the 109 // StructCodec registered. 110 func NewStructCodec(p StructTagParser, opts ...*bsonoptions.StructCodecOptions) (*StructCodec, error) { 111 if p == nil { 112 return nil, errors.New("a StructTagParser must be provided to NewStructCodec") 113 } 114 115 structOpt := bsonoptions.MergeStructCodecOptions(opts...) 116 117 codec := &StructCodec{ 118 cache: make(map[reflect.Type]*structDescription), 119 parser: p, 120 } 121 122 if structOpt.DecodeZeroStruct != nil { 123 codec.DecodeZeroStruct = *structOpt.DecodeZeroStruct 124 } 125 if structOpt.DecodeDeepZeroInline != nil { 126 codec.DecodeDeepZeroInline = *structOpt.DecodeDeepZeroInline 127 } 128 if structOpt.EncodeOmitDefaultStruct != nil { 129 codec.EncodeOmitDefaultStruct = *structOpt.EncodeOmitDefaultStruct 130 } 131 if structOpt.OverwriteDuplicatedInlinedFields != nil { 132 codec.OverwriteDuplicatedInlinedFields = *structOpt.OverwriteDuplicatedInlinedFields 133 } 134 if structOpt.AllowUnexportedFields != nil { 135 codec.AllowUnexportedFields = *structOpt.AllowUnexportedFields 136 } 137 138 return codec, nil 139 } 140 141 // EncodeValue handles encoding generic struct types. 142 func (sc *StructCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { 143 if !val.IsValid() || val.Kind() != reflect.Struct { 144 return ValueEncoderError{Name: "StructCodec.EncodeValue", Kinds: []reflect.Kind{reflect.Struct}, Received: val} 145 } 146 147 sd, err := sc.describeStruct(ec.Registry, val.Type(), ec.useJSONStructTags, ec.errorOnInlineDuplicates) 148 if err != nil { 149 return err 150 } 151 152 dw, err := vw.WriteDocument() 153 if err != nil { 154 return err 155 } 156 var rv reflect.Value 157 for _, desc := range sd.fl { 158 if desc.inline == nil { 159 rv = val.Field(desc.idx) 160 } else { 161 rv, err = fieldByIndexErr(val, desc.inline) 162 if err != nil { 163 continue 164 } 165 } 166 167 desc.encoder, rv, err = defaultValueEncoders.lookupElementEncoder(ec, desc.encoder, rv) 168 169 if err != nil && err != errInvalidValue { 170 return err 171 } 172 173 if err == errInvalidValue { 174 if desc.omitEmpty { 175 continue 176 } 177 vw2, err := dw.WriteDocumentElement(desc.name) 178 if err != nil { 179 return err 180 } 181 err = vw2.WriteNull() 182 if err != nil { 183 return err 184 } 185 continue 186 } 187 188 if desc.encoder == nil { 189 return ErrNoEncoder{Type: rv.Type()} 190 } 191 192 encoder := desc.encoder 193 194 var zero bool 195 rvInterface := rv.Interface() 196 if cz, ok := encoder.(CodecZeroer); ok { 197 zero = cz.IsTypeZero(rvInterface) 198 } else if rv.Kind() == reflect.Interface { 199 // isZero will not treat an interface rv as an interface, so we need to check for the 200 // zero interface separately. 201 zero = rv.IsNil() 202 } else { 203 zero = isZero(rvInterface, sc.EncodeOmitDefaultStruct || ec.omitZeroStruct) 204 } 205 if desc.omitEmpty && zero { 206 continue 207 } 208 209 vw2, err := dw.WriteDocumentElement(desc.name) 210 if err != nil { 211 return err 212 } 213 214 ectx := EncodeContext{ 215 Registry: ec.Registry, 216 MinSize: desc.minSize || ec.MinSize, 217 errorOnInlineDuplicates: ec.errorOnInlineDuplicates, 218 stringifyMapKeysWithFmt: ec.stringifyMapKeysWithFmt, 219 nilMapAsEmpty: ec.nilMapAsEmpty, 220 nilSliceAsEmpty: ec.nilSliceAsEmpty, 221 nilByteSliceAsEmpty: ec.nilByteSliceAsEmpty, 222 omitZeroStruct: ec.omitZeroStruct, 223 useJSONStructTags: ec.useJSONStructTags, 224 } 225 err = encoder.EncodeValue(ectx, vw2, rv) 226 if err != nil { 227 return err 228 } 229 } 230 231 if sd.inlineMap >= 0 { 232 rv := val.Field(sd.inlineMap) 233 collisionFn := func(key string) bool { 234 _, exists := sd.fm[key] 235 return exists 236 } 237 238 return defaultMapCodec.mapEncodeValue(ec, dw, rv, collisionFn) 239 } 240 241 return dw.WriteDocumentEnd() 242 } 243 244 func newDecodeError(key string, original error) error { 245 de, ok := original.(*DecodeError) 246 if !ok { 247 return &DecodeError{ 248 keys: []string{key}, 249 wrapped: original, 250 } 251 } 252 253 de.keys = append(de.keys, key) 254 return de 255 } 256 257 // DecodeValue implements the Codec interface. 258 // By default, map types in val will not be cleared. If a map has existing key/value pairs, it will be extended with the new ones from vr. 259 // For slices, the decoder will set the length of the slice to zero and append all elements. The underlying array will not be cleared. 260 func (sc *StructCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { 261 if !val.CanSet() || val.Kind() != reflect.Struct { 262 return ValueDecoderError{Name: "StructCodec.DecodeValue", Kinds: []reflect.Kind{reflect.Struct}, Received: val} 263 } 264 265 switch vrType := vr.Type(); vrType { 266 case bsontype.Type(0), bsontype.EmbeddedDocument: 267 case bsontype.Null: 268 if err := vr.ReadNull(); err != nil { 269 return err 270 } 271 272 val.Set(reflect.Zero(val.Type())) 273 return nil 274 case bsontype.Undefined: 275 if err := vr.ReadUndefined(); err != nil { 276 return err 277 } 278 279 val.Set(reflect.Zero(val.Type())) 280 return nil 281 default: 282 return fmt.Errorf("cannot decode %v into a %s", vrType, val.Type()) 283 } 284 285 sd, err := sc.describeStruct(dc.Registry, val.Type(), dc.useJSONStructTags, false) 286 if err != nil { 287 return err 288 } 289 290 if sc.DecodeZeroStruct || dc.zeroStructs { 291 val.Set(reflect.Zero(val.Type())) 292 } 293 if sc.DecodeDeepZeroInline && sd.inline { 294 val.Set(deepZero(val.Type())) 295 } 296 297 var decoder ValueDecoder 298 var inlineMap reflect.Value 299 if sd.inlineMap >= 0 { 300 inlineMap = val.Field(sd.inlineMap) 301 decoder, err = dc.LookupDecoder(inlineMap.Type().Elem()) 302 if err != nil { 303 return err 304 } 305 } 306 307 dr, err := vr.ReadDocument() 308 if err != nil { 309 return err 310 } 311 312 for { 313 name, vr, err := dr.ReadElement() 314 if err == bsonrw.ErrEOD { 315 break 316 } 317 if err != nil { 318 return err 319 } 320 321 fd, exists := sd.fm[name] 322 if !exists { 323 // if the original name isn't found in the struct description, try again with the name in lowercase 324 // this could match if a BSON tag isn't specified because by default, describeStruct lowercases all field 325 // names 326 fd, exists = sd.fm[strings.ToLower(name)] 327 } 328 329 if !exists { 330 if sd.inlineMap < 0 { 331 // The encoding/json package requires a flag to return on error for non-existent fields. 332 // This functionality seems appropriate for the struct codec. 333 err = vr.Skip() 334 if err != nil { 335 return err 336 } 337 continue 338 } 339 340 if inlineMap.IsNil() { 341 inlineMap.Set(reflect.MakeMap(inlineMap.Type())) 342 } 343 344 elem := reflect.New(inlineMap.Type().Elem()).Elem() 345 dc.Ancestor = inlineMap.Type() 346 err = decoder.DecodeValue(dc, vr, elem) 347 if err != nil { 348 return err 349 } 350 inlineMap.SetMapIndex(reflect.ValueOf(name), elem) 351 continue 352 } 353 354 var field reflect.Value 355 if fd.inline == nil { 356 field = val.Field(fd.idx) 357 } else { 358 field, err = getInlineField(val, fd.inline) 359 if err != nil { 360 return err 361 } 362 } 363 364 if !field.CanSet() { // Being settable is a super set of being addressable. 365 innerErr := fmt.Errorf("field %v is not settable", field) 366 return newDecodeError(fd.name, innerErr) 367 } 368 if field.Kind() == reflect.Ptr && field.IsNil() { 369 field.Set(reflect.New(field.Type().Elem())) 370 } 371 field = field.Addr() 372 373 dctx := DecodeContext{ 374 Registry: dc.Registry, 375 Truncate: fd.truncate || dc.Truncate, 376 defaultDocumentType: dc.defaultDocumentType, 377 binaryAsSlice: dc.binaryAsSlice, 378 useJSONStructTags: dc.useJSONStructTags, 379 useLocalTimeZone: dc.useLocalTimeZone, 380 zeroMaps: dc.zeroMaps, 381 zeroStructs: dc.zeroStructs, 382 } 383 384 if fd.decoder == nil { 385 return newDecodeError(fd.name, ErrNoDecoder{Type: field.Elem().Type()}) 386 } 387 388 err = fd.decoder.DecodeValue(dctx, vr, field.Elem()) 389 if err != nil { 390 return newDecodeError(fd.name, err) 391 } 392 } 393 394 return nil 395 } 396 397 func isZero(i interface{}, omitZeroStruct bool) bool { 398 v := reflect.ValueOf(i) 399 400 // check the value validity 401 if !v.IsValid() { 402 return true 403 } 404 405 if z, ok := v.Interface().(Zeroer); ok && (v.Kind() != reflect.Ptr || !v.IsNil()) { 406 return z.IsZero() 407 } 408 409 switch v.Kind() { 410 case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 411 return v.Len() == 0 412 case reflect.Bool: 413 return !v.Bool() 414 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 415 return v.Int() == 0 416 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 417 return v.Uint() == 0 418 case reflect.Float32, reflect.Float64: 419 return v.Float() == 0 420 case reflect.Interface, reflect.Ptr: 421 return v.IsNil() 422 case reflect.Struct: 423 if !omitZeroStruct { 424 return false 425 } 426 427 // TODO(GODRIVER-2820): Update the logic to be able to handle private struct fields. 428 // TODO Use condition "reflect.Zero(v.Type()).Equal(v)" instead. 429 430 vt := v.Type() 431 if vt == tTime { 432 return v.Interface().(time.Time).IsZero() 433 } 434 for i := 0; i < v.NumField(); i++ { 435 if vt.Field(i).PkgPath != "" && !vt.Field(i).Anonymous { 436 continue // Private field 437 } 438 fld := v.Field(i) 439 if !isZero(fld.Interface(), omitZeroStruct) { 440 return false 441 } 442 } 443 return true 444 } 445 446 return false 447 } 448 449 type structDescription struct { 450 fm map[string]fieldDescription 451 fl []fieldDescription 452 inlineMap int 453 inline bool 454 } 455 456 type fieldDescription struct { 457 name string // BSON key name 458 fieldName string // struct field name 459 idx int 460 omitEmpty bool 461 minSize bool 462 truncate bool 463 inline []int 464 encoder ValueEncoder 465 decoder ValueDecoder 466 } 467 468 type byIndex []fieldDescription 469 470 func (bi byIndex) Len() int { return len(bi) } 471 472 func (bi byIndex) Swap(i, j int) { bi[i], bi[j] = bi[j], bi[i] } 473 474 func (bi byIndex) Less(i, j int) bool { 475 // If a field is inlined, its index in the top level struct is stored at inline[0] 476 iIdx, jIdx := bi[i].idx, bi[j].idx 477 if len(bi[i].inline) > 0 { 478 iIdx = bi[i].inline[0] 479 } 480 if len(bi[j].inline) > 0 { 481 jIdx = bi[j].inline[0] 482 } 483 if iIdx != jIdx { 484 return iIdx < jIdx 485 } 486 for k, biik := range bi[i].inline { 487 if k >= len(bi[j].inline) { 488 return false 489 } 490 if biik != bi[j].inline[k] { 491 return biik < bi[j].inline[k] 492 } 493 } 494 return len(bi[i].inline) < len(bi[j].inline) 495 } 496 497 func (sc *StructCodec) describeStruct( 498 r *Registry, 499 t reflect.Type, 500 useJSONStructTags bool, 501 errorOnDuplicates bool, 502 ) (*structDescription, error) { 503 // We need to analyze the struct, including getting the tags, collecting 504 // information about inlining, and create a map of the field name to the field. 505 sc.l.RLock() 506 ds, exists := sc.cache[t] 507 sc.l.RUnlock() 508 if exists { 509 return ds, nil 510 } 511 512 numFields := t.NumField() 513 sd := &structDescription{ 514 fm: make(map[string]fieldDescription, numFields), 515 fl: make([]fieldDescription, 0, numFields), 516 inlineMap: -1, 517 } 518 519 var fields []fieldDescription 520 for i := 0; i < numFields; i++ { 521 sf := t.Field(i) 522 if sf.PkgPath != "" && (!sc.AllowUnexportedFields || !sf.Anonymous) { 523 // field is private or unexported fields aren't allowed, ignore 524 continue 525 } 526 527 sfType := sf.Type 528 encoder, err := r.LookupEncoder(sfType) 529 if err != nil { 530 encoder = nil 531 } 532 decoder, err := r.LookupDecoder(sfType) 533 if err != nil { 534 decoder = nil 535 } 536 537 description := fieldDescription{ 538 fieldName: sf.Name, 539 idx: i, 540 encoder: encoder, 541 decoder: decoder, 542 } 543 544 var stags StructTags 545 // If the caller requested that we use JSON struct tags, use the JSONFallbackStructTagParser 546 // instead of the parser defined on the codec. 547 if useJSONStructTags { 548 stags, err = JSONFallbackStructTagParser.ParseStructTags(sf) 549 } else { 550 stags, err = sc.parser.ParseStructTags(sf) 551 } 552 if err != nil { 553 return nil, err 554 } 555 if stags.Skip { 556 continue 557 } 558 description.name = stags.Name 559 description.omitEmpty = stags.OmitEmpty 560 description.minSize = stags.MinSize 561 description.truncate = stags.Truncate 562 563 if stags.Inline { 564 sd.inline = true 565 switch sfType.Kind() { 566 case reflect.Map: 567 if sd.inlineMap >= 0 { 568 return nil, errors.New("(struct " + t.String() + ") multiple inline maps") 569 } 570 if sfType.Key() != tString { 571 return nil, errors.New("(struct " + t.String() + ") inline map must have a string keys") 572 } 573 sd.inlineMap = description.idx 574 case reflect.Ptr: 575 sfType = sfType.Elem() 576 if sfType.Kind() != reflect.Struct { 577 return nil, fmt.Errorf("(struct %s) inline fields must be a struct, a struct pointer, or a map", t.String()) 578 } 579 fallthrough 580 case reflect.Struct: 581 inlinesf, err := sc.describeStruct(r, sfType, useJSONStructTags, errorOnDuplicates) 582 if err != nil { 583 return nil, err 584 } 585 for _, fd := range inlinesf.fl { 586 if fd.inline == nil { 587 fd.inline = []int{i, fd.idx} 588 } else { 589 fd.inline = append([]int{i}, fd.inline...) 590 } 591 fields = append(fields, fd) 592 593 } 594 default: 595 return nil, fmt.Errorf("(struct %s) inline fields must be a struct, a struct pointer, or a map", t.String()) 596 } 597 continue 598 } 599 fields = append(fields, description) 600 } 601 602 // Sort fieldDescriptions by name and use dominance rules to determine which should be added for each name 603 sort.Slice(fields, func(i, j int) bool { 604 x := fields 605 // sort field by name, breaking ties with depth, then 606 // breaking ties with index sequence. 607 if x[i].name != x[j].name { 608 return x[i].name < x[j].name 609 } 610 if len(x[i].inline) != len(x[j].inline) { 611 return len(x[i].inline) < len(x[j].inline) 612 } 613 return byIndex(x).Less(i, j) 614 }) 615 616 for advance, i := 0, 0; i < len(fields); i += advance { 617 // One iteration per name. 618 // Find the sequence of fields with the name of this first field. 619 fi := fields[i] 620 name := fi.name 621 for advance = 1; i+advance < len(fields); advance++ { 622 fj := fields[i+advance] 623 if fj.name != name { 624 break 625 } 626 } 627 if advance == 1 { // Only one field with this name 628 sd.fl = append(sd.fl, fi) 629 sd.fm[name] = fi 630 continue 631 } 632 dominant, ok := dominantField(fields[i : i+advance]) 633 if !ok || !sc.OverwriteDuplicatedInlinedFields || errorOnDuplicates { 634 return nil, fmt.Errorf("struct %s has duplicated key %s", t.String(), name) 635 } 636 sd.fl = append(sd.fl, dominant) 637 sd.fm[name] = dominant 638 } 639 640 sort.Sort(byIndex(sd.fl)) 641 642 sc.l.Lock() 643 sc.cache[t] = sd 644 sc.l.Unlock() 645 646 return sd, nil 647 } 648 649 // dominantField looks through the fields, all of which are known to 650 // have the same name, to find the single field that dominates the 651 // others using Go's inlining rules. If there are multiple top-level 652 // fields, the boolean will be false: This condition is an error in Go 653 // and we skip all the fields. 654 func dominantField(fields []fieldDescription) (fieldDescription, bool) { 655 // The fields are sorted in increasing index-length order, then by presence of tag. 656 // That means that the first field is the dominant one. We need only check 657 // for error cases: two fields at top level. 658 if len(fields) > 1 && 659 len(fields[0].inline) == len(fields[1].inline) { 660 return fieldDescription{}, false 661 } 662 return fields[0], true 663 } 664 665 func fieldByIndexErr(v reflect.Value, index []int) (result reflect.Value, err error) { 666 defer func() { 667 if recovered := recover(); recovered != nil { 668 switch r := recovered.(type) { 669 case string: 670 err = fmt.Errorf("%s", r) 671 case error: 672 err = r 673 } 674 } 675 }() 676 677 result = v.FieldByIndex(index) 678 return 679 } 680 681 func getInlineField(val reflect.Value, index []int) (reflect.Value, error) { 682 field, err := fieldByIndexErr(val, index) 683 if err == nil { 684 return field, nil 685 } 686 687 // if parent of this element doesn't exist, fix its parent 688 inlineParent := index[:len(index)-1] 689 var fParent reflect.Value 690 if fParent, err = fieldByIndexErr(val, inlineParent); err != nil { 691 fParent, err = getInlineField(val, inlineParent) 692 if err != nil { 693 return fParent, err 694 } 695 } 696 fParent.Set(reflect.New(fParent.Type().Elem())) 697 698 return fieldByIndexErr(val, index) 699 } 700 701 // DeepZero returns recursive zero object 702 func deepZero(st reflect.Type) (result reflect.Value) { 703 result = reflect.Indirect(reflect.New(st)) 704 705 if result.Kind() == reflect.Struct { 706 for i := 0; i < result.NumField(); i++ { 707 if f := result.Field(i); f.Kind() == reflect.Ptr { 708 if f.CanInterface() { 709 if ft := reflect.TypeOf(f.Interface()); ft.Elem().Kind() == reflect.Struct { 710 result.Field(i).Set(recursivePointerTo(deepZero(ft.Elem()))) 711 } 712 } 713 } 714 } 715 } 716 717 return 718 } 719 720 // recursivePointerTo calls reflect.New(v.Type) but recursively for its fields inside 721 func recursivePointerTo(v reflect.Value) reflect.Value { 722 v = reflect.Indirect(v) 723 result := reflect.New(v.Type()) 724 if v.Kind() == reflect.Struct { 725 for i := 0; i < v.NumField(); i++ { 726 if f := v.Field(i); f.Kind() == reflect.Ptr { 727 if f.Elem().Kind() == reflect.Struct { 728 result.Elem().Field(i).Set(recursivePointerTo(f)) 729 } 730 } 731 } 732 } 733 734 return result 735 }