copy.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package awsutil
  2. import (
  3. "io"
  4. "reflect"
  5. "time"
  6. )
  7. // Copy deeply copies a src structure to dst. Useful for copying request and
  8. // response structures.
  9. //
  10. // Can copy between structs of different type, but will only copy fields which
  11. // are assignable, and exist in both structs. Fields which are not assignable,
  12. // or do not exist in both structs are ignored.
  13. func Copy(dst, src interface{}) {
  14. dstval := reflect.ValueOf(dst)
  15. if !dstval.IsValid() {
  16. panic("Copy dst cannot be nil")
  17. }
  18. rcopy(dstval, reflect.ValueOf(src), true)
  19. }
  20. // CopyOf returns a copy of src while also allocating the memory for dst.
  21. // src must be a pointer type or this operation will fail.
  22. func CopyOf(src interface{}) (dst interface{}) {
  23. dsti := reflect.New(reflect.TypeOf(src).Elem())
  24. dst = dsti.Interface()
  25. rcopy(dsti, reflect.ValueOf(src), true)
  26. return
  27. }
  28. // rcopy performs a recursive copy of values from the source to destination.
  29. //
  30. // root is used to skip certain aspects of the copy which are not valid
  31. // for the root node of a object.
  32. func rcopy(dst, src reflect.Value, root bool) {
  33. if !src.IsValid() {
  34. return
  35. }
  36. switch src.Kind() {
  37. case reflect.Ptr:
  38. if _, ok := src.Interface().(io.Reader); ok {
  39. if dst.Kind() == reflect.Ptr && dst.Elem().CanSet() {
  40. dst.Elem().Set(src)
  41. } else if dst.CanSet() {
  42. dst.Set(src)
  43. }
  44. } else {
  45. e := src.Type().Elem()
  46. if dst.CanSet() && !src.IsNil() {
  47. if _, ok := src.Interface().(*time.Time); !ok {
  48. dst.Set(reflect.New(e))
  49. } else {
  50. tempValue := reflect.New(e)
  51. tempValue.Elem().Set(src.Elem())
  52. // Sets time.Time's unexported values
  53. dst.Set(tempValue)
  54. }
  55. }
  56. if src.Elem().IsValid() {
  57. // Keep the current root state since the depth hasn't changed
  58. rcopy(dst.Elem(), src.Elem(), root)
  59. }
  60. }
  61. case reflect.Struct:
  62. t := dst.Type()
  63. for i := 0; i < t.NumField(); i++ {
  64. name := t.Field(i).Name
  65. srcVal := src.FieldByName(name)
  66. dstVal := dst.FieldByName(name)
  67. if srcVal.IsValid() && dstVal.CanSet() {
  68. rcopy(dstVal, srcVal, false)
  69. }
  70. }
  71. case reflect.Slice:
  72. if src.IsNil() {
  73. break
  74. }
  75. s := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
  76. dst.Set(s)
  77. for i := 0; i < src.Len(); i++ {
  78. rcopy(dst.Index(i), src.Index(i), false)
  79. }
  80. case reflect.Map:
  81. if src.IsNil() {
  82. break
  83. }
  84. s := reflect.MakeMap(src.Type())
  85. dst.Set(s)
  86. for _, k := range src.MapKeys() {
  87. v := src.MapIndex(k)
  88. v2 := reflect.New(v.Type()).Elem()
  89. rcopy(v2, v, false)
  90. dst.SetMapIndex(k, v2)
  91. }
  92. default:
  93. // Assign the value if possible. If its not assignable, the value would
  94. // need to be converted and the impact of that may be unexpected, or is
  95. // not compatible with the dst type.
  96. if src.Type().AssignableTo(dst.Type()) {
  97. dst.Set(src)
  98. }
  99. }
  100. }