1package copier 2 3import ( 4 "database/sql" 5 "errors" 6 "reflect" 7) 8 9// Copy copy things 10func Copy(toValue interface{}, fromValue interface{}) (err error) { 11 var ( 12 isSlice bool 13 amount = 1 14 from = indirect(reflect.ValueOf(fromValue)) 15 to = indirect(reflect.ValueOf(toValue)) 16 ) 17 18 if !to.CanAddr() { 19 return errors.New("copy to value is unaddressable") 20 } 21 22 // Return is from value is invalid 23 if !from.IsValid() { 24 return 25 } 26 27 // Just set it if possible to assign 28 if from.Type().AssignableTo(to.Type()) { 29 to.Set(from) 30 return 31 } 32 33 fromType := indirectType(from.Type()) 34 toType := indirectType(to.Type()) 35 36 if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct { 37 return 38 } 39 40 if to.Kind() == reflect.Slice { 41 isSlice = true 42 if from.Kind() == reflect.Slice { 43 amount = from.Len() 44 } 45 } 46 47 for i := 0; i < amount; i++ { 48 var dest, source reflect.Value 49 50 if isSlice { 51 // source 52 if from.Kind() == reflect.Slice { 53 source = indirect(from.Index(i)) 54 } else { 55 source = indirect(from) 56 } 57 58 // dest 59 dest = indirect(reflect.New(toType).Elem()) 60 } else { 61 source = indirect(from) 62 dest = indirect(to) 63 } 64 65 // Copy from field to field or method 66 for _, field := range deepFields(fromType) { 67 name := field.Name 68 69 if fromField := source.FieldByName(name); fromField.IsValid() { 70 // has field 71 if toField := dest.FieldByName(name); toField.IsValid() { 72 if toField.CanSet() { 73 if !set(toField, fromField) { 74 if err := Copy(toField.Addr().Interface(), fromField.Interface()); err != nil { 75 return err 76 } 77 } 78 } 79 } else { 80 // try to set to method 81 var toMethod reflect.Value 82 if dest.CanAddr() { 83 toMethod = dest.Addr().MethodByName(name) 84 } else { 85 toMethod = dest.MethodByName(name) 86 } 87 88 if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) { 89 toMethod.Call([]reflect.Value{fromField}) 90 } 91 } 92 } 93 } 94 95 // Copy from method to field 96 for _, field := range deepFields(toType) { 97 name := field.Name 98 99 var fromMethod reflect.Value 100 if source.CanAddr() { 101 fromMethod = source.Addr().MethodByName(name) 102 } else { 103 fromMethod = source.MethodByName(name) 104 } 105 106 if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 { 107 if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() { 108 values := fromMethod.Call([]reflect.Value{}) 109 if len(values) >= 1 { 110 set(toField, values[0]) 111 } 112 } 113 } 114 } 115 116 if isSlice { 117 if dest.Addr().Type().AssignableTo(to.Type().Elem()) { 118 to.Set(reflect.Append(to, dest.Addr())) 119 } else if dest.Type().AssignableTo(to.Type().Elem()) { 120 to.Set(reflect.Append(to, dest)) 121 } 122 } 123 } 124 return 125} 126 127func deepFields(reflectType reflect.Type) []reflect.StructField { 128 var fields []reflect.StructField 129 130 if reflectType = indirectType(reflectType); reflectType.Kind() == reflect.Struct { 131 for i := 0; i < reflectType.NumField(); i++ { 132 v := reflectType.Field(i) 133 if v.Anonymous { 134 fields = append(fields, deepFields(v.Type)...) 135 } else { 136 fields = append(fields, v) 137 } 138 } 139 } 140 141 return fields 142} 143 144func indirect(reflectValue reflect.Value) reflect.Value { 145 for reflectValue.Kind() == reflect.Ptr { 146 reflectValue = reflectValue.Elem() 147 } 148 return reflectValue 149} 150 151func indirectType(reflectType reflect.Type) reflect.Type { 152 for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice { 153 reflectType = reflectType.Elem() 154 } 155 return reflectType 156} 157 158func set(to, from reflect.Value) bool { 159 if from.IsValid() { 160 if to.Kind() == reflect.Ptr { 161 //set `to` to nil if from is nil 162 if from.Kind() == reflect.Ptr && from.IsNil() { 163 to.Set(reflect.Zero(to.Type())) 164 return true 165 } else if to.IsNil() { 166 to.Set(reflect.New(to.Type().Elem())) 167 } 168 to = to.Elem() 169 } 170 171 if from.Type().ConvertibleTo(to.Type()) { 172 to.Set(from.Convert(to.Type())) 173 } else if scanner, ok := to.Addr().Interface().(sql.Scanner); ok { 174 err := scanner.Scan(from.Interface()) 175 if err != nil { 176 return false 177 } 178 } else if from.Kind() == reflect.Ptr { 179 return set(to, from.Elem()) 180 } else { 181 return false 182 } 183 } 184 return true 185} 186