55 lines
1.5 KiB
Go
55 lines
1.5 KiB
Go
package blackmagic
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// AssignIfCompatible is a convenience function to safely
|
|
// assign arbitrary values. dst must be a pointer to an
|
|
// empty interface, or it must be a pointer to a compatible
|
|
// variable type that can hold src.
|
|
func AssignIfCompatible(dst, src interface{}) error {
|
|
orv := reflect.ValueOf(src) // save this value for error reporting
|
|
result := orv
|
|
|
|
// t can be a pointer or a slice, and the code will slightly change
|
|
// depending on this
|
|
var isSlice bool
|
|
switch result.Kind() {
|
|
case reflect.Ptr:
|
|
// no op
|
|
case reflect.Slice:
|
|
isSlice = true
|
|
default:
|
|
return errors.Errorf("argument t to AssignIfCompatible must be a pointer or a slice: %T", src)
|
|
}
|
|
|
|
rv := reflect.ValueOf(dst)
|
|
if rv.Kind() != reflect.Ptr {
|
|
return errors.Errorf(`argument to AssignIfCompatible() must be a pointer: %T`, dst)
|
|
}
|
|
|
|
actualDst := rv.Elem()
|
|
switch actualDst.Kind() {
|
|
case reflect.Interface:
|
|
// If it's an interface, we can just assign the pointer to the interface{}
|
|
default:
|
|
// If it's a pointer to the struct we're looking for, we need to set
|
|
// the de-referenced struct
|
|
if !isSlice {
|
|
result = result.Elem()
|
|
}
|
|
}
|
|
if !result.Type().AssignableTo(actualDst.Type()) {
|
|
return errors.Errorf(`argument to AssignIfCompatible() must be compatible with %T (was %T)`, orv.Interface(), dst)
|
|
}
|
|
|
|
if !actualDst.CanSet() {
|
|
return errors.Errorf(`argument to AssignIfCompatible() must be settable`)
|
|
}
|
|
actualDst.Set(result)
|
|
|
|
return nil
|
|
}
|