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