github.com/wangyougui/gf/v2@v2.6.5/util/gconv/gconv_structs.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  package gconv
     8  
     9  import (
    10  	"reflect"
    11  
    12  	"github.com/wangyougui/gf/v2/errors/gcode"
    13  	"github.com/wangyougui/gf/v2/errors/gerror"
    14  )
    15  
    16  // Structs converts any slice to given struct slice.
    17  // Also see Scan, Struct.
    18  func Structs(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) {
    19  	return Scan(params, pointer, paramKeyToAttrMap...)
    20  }
    21  
    22  // SliceStruct is alias of Structs.
    23  func SliceStruct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
    24  	return Structs(params, pointer, mapping...)
    25  }
    26  
    27  // StructsTag acts as Structs but also with support for priority tag feature, which retrieves the
    28  // specified tags for `params` key-value items to struct attribute names mapping.
    29  // The parameter `priorityTag` supports multiple tags that can be joined with char ','.
    30  func StructsTag(params interface{}, pointer interface{}, priorityTag string) (err error) {
    31  	return doStructs(params, pointer, nil, priorityTag)
    32  }
    33  
    34  // doStructs converts any slice to given struct slice.
    35  //
    36  // It automatically checks and converts json string to []map if `params` is string/[]byte.
    37  //
    38  // The parameter `pointer` should be type of pointer to slice of struct.
    39  // Note that if `pointer` is a pointer to another pointer of type of slice of struct,
    40  // it will create the struct/pointer internally.
    41  func doStructs(
    42  	params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string,
    43  ) (err error) {
    44  	defer func() {
    45  		// Catch the panic, especially the reflection operation panics.
    46  		if exception := recover(); exception != nil {
    47  			if v, ok := exception.(error); ok && gerror.HasStack(v) {
    48  				err = v
    49  			} else {
    50  				err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
    51  			}
    52  		}
    53  	}()
    54  
    55  	// Pointer type check.
    56  	pointerRv, ok := pointer.(reflect.Value)
    57  	if !ok {
    58  		pointerRv = reflect.ValueOf(pointer)
    59  		if kind := pointerRv.Kind(); kind != reflect.Ptr {
    60  			return gerror.NewCodef(
    61  				gcode.CodeInvalidParameter,
    62  				"pointer should be type of pointer, but got: %v", kind,
    63  			)
    64  		}
    65  	}
    66  	// Converting `params` to map slice.
    67  	var (
    68  		paramsList []interface{}
    69  		paramsRv   = reflect.ValueOf(params)
    70  		paramsKind = paramsRv.Kind()
    71  	)
    72  	for paramsKind == reflect.Ptr {
    73  		paramsRv = paramsRv.Elem()
    74  		paramsKind = paramsRv.Kind()
    75  	}
    76  	switch paramsKind {
    77  	case reflect.Slice, reflect.Array:
    78  		paramsList = make([]interface{}, paramsRv.Len())
    79  		for i := 0; i < paramsRv.Len(); i++ {
    80  			paramsList[i] = paramsRv.Index(i).Interface()
    81  		}
    82  	default:
    83  		var paramsMaps = Maps(params)
    84  		paramsList = make([]interface{}, len(paramsMaps))
    85  		for i := 0; i < len(paramsMaps); i++ {
    86  			paramsList[i] = paramsMaps[i]
    87  		}
    88  	}
    89  	// If `params` is an empty slice, no conversion.
    90  	if len(paramsList) == 0 {
    91  		return nil
    92  	}
    93  	var (
    94  		reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsList), len(paramsList))
    95  		itemType         = reflectElemArray.Index(0).Type()
    96  		itemTypeKind     = itemType.Kind()
    97  		pointerRvElem    = pointerRv.Elem()
    98  		pointerRvLength  = pointerRvElem.Len()
    99  	)
   100  	if itemTypeKind == reflect.Ptr {
   101  		// Pointer element.
   102  		for i := 0; i < len(paramsList); i++ {
   103  			var tempReflectValue reflect.Value
   104  			if i < pointerRvLength {
   105  				// Might be nil.
   106  				tempReflectValue = pointerRvElem.Index(i).Elem()
   107  			}
   108  			if !tempReflectValue.IsValid() {
   109  				tempReflectValue = reflect.New(itemType.Elem()).Elem()
   110  			}
   111  			if err = doStruct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil {
   112  				return err
   113  			}
   114  			reflectElemArray.Index(i).Set(tempReflectValue.Addr())
   115  		}
   116  	} else {
   117  		// Struct element.
   118  		for i := 0; i < len(paramsList); i++ {
   119  			var tempReflectValue reflect.Value
   120  			if i < pointerRvLength {
   121  				tempReflectValue = pointerRvElem.Index(i)
   122  			} else {
   123  				tempReflectValue = reflect.New(itemType).Elem()
   124  			}
   125  			if err = doStruct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil {
   126  				return err
   127  			}
   128  			reflectElemArray.Index(i).Set(tempReflectValue)
   129  		}
   130  	}
   131  	pointerRv.Elem().Set(reflectElemArray)
   132  	return nil
   133  }