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