github.com/altipla-consulting/ravendb-go-client@v0.1.3/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  }