| package awsutil |
| |
| import ( |
| "io" |
| "reflect" |
| "time" |
| ) |
| |
| // Copy deeply copies a src structure to dst. Useful for copying request and |
| // response structures. |
| // |
| // Can copy between structs of different type, but will only copy fields which |
| // are assignable, and exist in both structs. Fields which are not assignable, |
| // or do not exist in both structs are ignored. |
| func Copy(dst, src interface{}) { |
| dstval := reflect.ValueOf(dst) |
| if !dstval.IsValid() { |
| panic("Copy dst cannot be nil") |
| } |
| |
| rcopy(dstval, reflect.ValueOf(src), true) |
| } |
| |
| // CopyOf returns a copy of src while also allocating the memory for dst. |
| // src must be a pointer type or this operation will fail. |
| func CopyOf(src interface{}) (dst interface{}) { |
| dsti := reflect.New(reflect.TypeOf(src).Elem()) |
| dst = dsti.Interface() |
| rcopy(dsti, reflect.ValueOf(src), true) |
| return |
| } |
| |
| // rcopy performs a recursive copy of values from the source to destination. |
| // |
| // root is used to skip certain aspects of the copy which are not valid |
| // for the root node of a object. |
| func rcopy(dst, src reflect.Value, root bool) { |
| if !src.IsValid() { |
| return |
| } |
| |
| switch src.Kind() { |
| case reflect.Ptr: |
| if _, ok := src.Interface().(io.Reader); ok { |
| if dst.Kind() == reflect.Ptr && dst.Elem().CanSet() { |
| dst.Elem().Set(src) |
| } else if dst.CanSet() { |
| dst.Set(src) |
| } |
| } else { |
| e := src.Type().Elem() |
| if dst.CanSet() && !src.IsNil() { |
| if _, ok := src.Interface().(*time.Time); !ok { |
| dst.Set(reflect.New(e)) |
| } else { |
| tempValue := reflect.New(e) |
| tempValue.Elem().Set(src.Elem()) |
| // Sets time.Time's unexported values |
| dst.Set(tempValue) |
| } |
| } |
| if src.Elem().IsValid() { |
| // Keep the current root state since the depth hasn't changed |
| rcopy(dst.Elem(), src.Elem(), root) |
| } |
| } |
| case reflect.Struct: |
| t := dst.Type() |
| for i := 0; i < t.NumField(); i++ { |
| name := t.Field(i).Name |
| srcVal := src.FieldByName(name) |
| dstVal := dst.FieldByName(name) |
| if srcVal.IsValid() && dstVal.CanSet() { |
| rcopy(dstVal, srcVal, false) |
| } |
| } |
| case reflect.Slice: |
| if src.IsNil() { |
| break |
| } |
| |
| s := reflect.MakeSlice(src.Type(), src.Len(), src.Cap()) |
| dst.Set(s) |
| for i := 0; i < src.Len(); i++ { |
| rcopy(dst.Index(i), src.Index(i), false) |
| } |
| case reflect.Map: |
| if src.IsNil() { |
| break |
| } |
| |
| s := reflect.MakeMap(src.Type()) |
| dst.Set(s) |
| for _, k := range src.MapKeys() { |
| v := src.MapIndex(k) |
| v2 := reflect.New(v.Type()).Elem() |
| rcopy(v2, v, false) |
| dst.SetMapIndex(k, v2) |
| } |
| default: |
| // Assign the value if possible. If its not assignable, the value would |
| // need to be converted and the impact of that may be unexpected, or is |
| // not compatible with the dst type. |
| if src.Type().AssignableTo(dst.Type()) { |
| dst.Set(src) |
| } |
| } |
| } |