gitlab.com/evatix-go/core@v1.3.55/coredata/coredynamic/ReflectSetFromTo.go (about)

     1  package coredynamic
     2  
     3  import (
     4  	"encoding/json"
     5  	"reflect"
     6  
     7  	"gitlab.com/evatix-go/core/coredata/corejson"
     8  	"gitlab.com/evatix-go/core/errcore"
     9  	"gitlab.com/evatix-go/core/internal/reflectinternal"
    10  	"gitlab.com/evatix-go/core/isany"
    11  )
    12  
    13  // ReflectSetFromTo
    14  //
    15  // Set any object from to toPointer object
    16  //
    17  // Valid Inputs or Supported (https://t.ly/1Lpt):
    18  //  - From, To: (null, null)                          -- do nothing
    19  //  - From, To: (sameTypePointer, sameTypePointer)    -- try reflection
    20  //  - From, To: (sameTypeNonPointer, sameTypePointer) -- try reflection
    21  //  - From, To: ([]byte or *[]byte, otherType)        -- try unmarshal, reflect
    22  //  - From, To: (otherType, *[]byte)                  -- try marshal, reflect
    23  //
    24  // Validations:
    25  //  - Check null, if both null no error return quickly.
    26  //  - NotSupported returns as error.
    27  //      - NotSupported: (from, to) - (..., not pointer)
    28  //      - NotSupported: (from, to) - (null, notNull)
    29  //      - NotSupported: (from, to) - (notNull, null)
    30  //      - NotSupported: (from, to) - not same type and not bytes on any
    31  //  - `From` null or nil is not supported and will return error.
    32  //
    33  // Reference:
    34  //  - Reflection String Set Example : https://go.dev/play/p/fySLYuOvoRK.go?download=true
    35  //  - Method document screenshot    : https://prnt.sc/26dmf5g
    36  func ReflectSetFromTo(
    37  	from,
    38  	toPointer interface{},
    39  ) error {
    40  	isLeftNull, isRightNull := isany.NullLeftRight(
    41  		from,
    42  		toPointer)
    43  
    44  	if isLeftNull == isRightNull && isLeftNull {
    45  		return nil
    46  	}
    47  
    48  	leftRfType := reflect.TypeOf(from)
    49  	rightRfType := reflect.TypeOf(toPointer)
    50  
    51  	if isRightNull {
    52  		return errcore.
    53  			InvalidNullPointerType.
    54  			MsgCsvRefError(
    55  				"\"destination pointer is null, cannot proceed further!\""+supportedTypesMessageReference,
    56  				"FromType", leftRfType, "ToType", rightRfType)
    57  	}
    58  
    59  	rightKind := rightRfType.Kind()
    60  	isRightKindNotPointer := rightKind != reflect.Ptr
    61  	if isRightKindNotPointer {
    62  		return errcore.UnexpectedType.
    63  			MsgCsvRefError(
    64  				"\"destination or toPointer must be a pointer to set!\""+supportedTypesMessageReference,
    65  				"FromType", leftRfType, "ToType", rightRfType)
    66  	}
    67  
    68  	isSameType := leftRfType == rightRfType
    69  	leftRv := reflect.ValueOf(from)
    70  	rightRv := reflect.ValueOf(toPointer) // right is pointer confirmed by previous validation
    71  	isLeftAnyNull := reflectinternal.IsNullUsingReflectValue(leftRv) ||
    72  		reflectinternal.IsNull(leftRfType)
    73  
    74  	if isLeftAnyNull {
    75  		return errcore.
    76  			InvalidValueType.
    77  			SrcDestinationErr(
    78  				"`from` is nil, cannot set null or nil to destination.\"!"+supportedTypesMessageReference,
    79  				"FromType", leftRfType,
    80  				"ToType", rightRfType)
    81  	}
    82  
    83  	isLeftBytes := leftRfType == emptyBytesType
    84  	isLeftPointerBytes := leftRfType == emptyBytesPointerType
    85  	isLeftBytesOrPointerBytes := isLeftBytes || isLeftPointerBytes
    86  	isRightBytesPointer := rightRfType == emptyBytesPointerType
    87  	isAnyBytes := isLeftBytesOrPointerBytes || isRightBytesPointer
    88  
    89  	// case : From, To  : (sameTypePointer, sameTypePointer)    -- try reflection
    90  	if leftRfType == rightRfType {
    91  		// reflect, both same
    92  		rightRv.Elem().Set(leftRv.Elem())
    93  
    94  		return nil
    95  	}
    96  
    97  	// case : From, To  : (sameTypeNonPointer, sameTypePointer) -- try reflection
    98  	if leftRfType.Kind() != reflect.Ptr && !isLeftNull && leftRfType == rightRfType.Elem() {
    99  		rightRv.Elem().Set(leftRv)
   100  
   101  		return nil
   102  	}
   103  
   104  	isNotSupportedType := !(isSameType || isAnyBytes)
   105  
   106  	if isNotSupportedType {
   107  		return errcore.
   108  			TypeMismatchType.
   109  			SrcDestinationErr(
   110  				"supported: \"types are same pointer or any bytes or destination is pointer.\"!"+supportedTypesMessageReference,
   111  				"FromType", leftRfType,
   112  				"ToType", rightRfType)
   113  	}
   114  
   115  	// case : From, To  : ([]byte or *[]byte, otherType)  -- try unmarshal, reflect
   116  	if isLeftBytes {
   117  		return corejson.
   118  			Deserialize.
   119  			UsingBytes(
   120  				from.([]byte),
   121  				toPointer)
   122  	}
   123  
   124  	// case : From, To  : (*[]byte, otherType) -- try unmarshal, reflect
   125  	if isLeftPointerBytes {
   126  		return corejson.
   127  			Deserialize.
   128  			UsingBytesPointer(
   129  				from.(*[]byte),
   130  				toPointer)
   131  	}
   132  
   133  	// case : From, To: (otherType, *[]byte) -- try marshal, reflect
   134  	var rawBytes []byte
   135  	var finalErr error
   136  
   137  	if isRightBytesPointer {
   138  		rawBytes, finalErr = json.Marshal(from)
   139  	}
   140  
   141  	if finalErr != nil {
   142  		return errcore.
   143  			MarshallingFailedType.
   144  			SrcDestinationErr(
   145  				finalErr.Error(),
   146  				"FromType", leftRfType,
   147  				"ToType", rightRfType)
   148  	}
   149  
   150  	err := json.Unmarshal(
   151  		rawBytes,
   152  		toPointer)
   153  
   154  	if err == nil {
   155  		return nil
   156  	}
   157  
   158  	// has error
   159  	return errcore.
   160  		UnMarshallingFailedType.
   161  		SrcDestinationErr(
   162  			err.Error(),
   163  			"FromType", leftRfType,
   164  			"ToType", rightRfType)
   165  }