...

Source file src/github.com/lestrrat-go/blackmagic/blackmagic.go

Documentation: github.com/lestrrat-go/blackmagic

     1  package blackmagic
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  )
     7  
     8  // AssignField is a convenience function to assign a value to
     9  // an optional struct field. In Go, an optional struct field is
    10  // usually denoted by a pointer to T instead of T:
    11  //
    12  //	type Object struct {
    13  //	  Optional *T
    14  //	}
    15  //
    16  // This gets a bit cumbersome when you want to assign literals
    17  // or you do not want to worry about taking the address of a
    18  // variable.
    19  //
    20  //	Object.Optional = &"foo" // doesn't compile!
    21  //
    22  // Instead you can use this function to do it in one line:
    23  //
    24  //	blackmagic.AssignOptionalField(&Object.Optionl, "foo")
    25  func AssignOptionalField(dst, src interface{}) error {
    26  	dstRV := reflect.ValueOf(dst)
    27  	srcRV := reflect.ValueOf(src)
    28  	if dstRV.Kind() != reflect.Pointer || dstRV.Elem().Kind() != reflect.Pointer {
    29  		return fmt.Errorf(`dst must be a pointer to a field that is turn a pointer of src (%T)`, src)
    30  	}
    31  
    32  	if !dstRV.Elem().CanSet() {
    33  		return fmt.Errorf(`dst (%T) is not assignable`, dstRV.Elem().Interface())
    34  	}
    35  	if !reflect.PtrTo(srcRV.Type()).AssignableTo(dstRV.Elem().Type()) {
    36  		return fmt.Errorf(`cannot assign src (%T) to dst (%T)`, src, dst)
    37  	}
    38  
    39  	ptr := reflect.New(srcRV.Type())
    40  	ptr.Elem().Set(srcRV)
    41  	dstRV.Elem().Set(ptr)
    42  	return nil
    43  }
    44  
    45  // AssignIfCompatible is a convenience function to safely
    46  // assign arbitrary values. dst must be a pointer to an
    47  // empty interface, or it must be a pointer to a compatible
    48  // variable type that can hold src.
    49  func AssignIfCompatible(dst, src interface{}) error {
    50  	orv := reflect.ValueOf(src) // save this value for error reporting
    51  	result := orv
    52  
    53  	// t can be a pointer or a slice, and the code will slightly change
    54  	// depending on this
    55  	var isPtr bool
    56  	var isSlice bool
    57  	switch result.Kind() {
    58  	case reflect.Ptr:
    59  		isPtr = true
    60  	case reflect.Slice:
    61  		isSlice = true
    62  	}
    63  
    64  	rv := reflect.ValueOf(dst)
    65  	if rv.Kind() != reflect.Ptr {
    66  		return fmt.Errorf(`destination argument to AssignIfCompatible() must be a pointer: %T`, dst)
    67  	}
    68  
    69  	actualDst := rv.Elem()
    70  	switch actualDst.Kind() {
    71  	case reflect.Interface:
    72  		// If it's an interface, we can just assign the pointer to the interface{}
    73  	default:
    74  		// If it's a pointer to the struct we're looking for, we need to set
    75  		// the de-referenced struct
    76  		if !isSlice && isPtr {
    77  			result = result.Elem()
    78  		}
    79  	}
    80  	if !result.Type().AssignableTo(actualDst.Type()) {
    81  		return fmt.Errorf(`argument to AssignIfCompatible() must be compatible with %T (was %T)`, orv.Interface(), dst)
    82  	}
    83  
    84  	if !actualDst.CanSet() {
    85  		return fmt.Errorf(`argument to AssignIfCompatible() must be settable`)
    86  	}
    87  	actualDst.Set(result)
    88  
    89  	return nil
    90  }
    91  

View as plain text