github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekastr/to_s.go (about)

     1  // Copyright © 2020. All rights reserved.
     2  // Author: Ilya Stroy.
     3  // Contacts: iyuryevich@pm.me, https://github.com/qioalice
     4  // License: https://opensource.org/licenses/MIT
     5  
     6  package ekastr
     7  
     8  import (
     9  	"fmt"
    10  	"reflect"
    11  	"strconv"
    12  	"unsafe"
    13  
    14  	"github.com/qioalice/ekago/v3/internal/ekaclike"
    15  
    16  	"github.com/modern-go/reflect2"
    17  )
    18  
    19  //goland:noinspection GoSnakeCaseUsage
    20  const (
    21  	TO_S_HANDLE_ARRAYS   uint8 = 0x01
    22  	TO_S_HANDLE_STRUCTS  uint8 = 0x02
    23  	TO_S_HANDLE_MAPS     uint8 = 0x04
    24  	TO_S_DEREFERENCE_PTR uint8 = 0x08
    25  )
    26  
    27  /*
    28  ToString is a special type that allows you to get a string representation
    29  of any type's passed argument. It returns an empty string for nil interface.
    30  */
    31  func ToString(i any) string {
    32  	iface := ekaclike.UnpackInterface(i)
    33  	return ToStringUnsafe(iface.Type, iface.Word, 0xFF)
    34  }
    35  
    36  /*
    37  ToStringUnsafe is the same as ToString but allows you to reject handling
    38  arrays, structs, maps, pointers. It also awaits to get unpacked Golang interface
    39  as both of rtype's and data's pointers. Zero safe - returns an empty string then.
    40  */
    41  func ToStringUnsafe(rtype uintptr, word unsafe.Pointer, mask uint8) string {
    42  
    43  	if word == nil && rtype != 0 {
    44  		switch rtype {
    45  		case ekaclike.RTypeBool, ekaclike.RTypeString, ekaclike.RTypeBytes:
    46  			return ""
    47  		case ekaclike.RTypeInt,
    48  			ekaclike.RTypeInt8, ekaclike.RTypeInt16,
    49  			ekaclike.RTypeInt32, ekaclike.RTypeInt64,
    50  			ekaclike.RTypeUint,
    51  			ekaclike.RTypeUint8, ekaclike.RTypeUint16,
    52  			ekaclike.RTypeUint32, ekaclike.RTypeUint64,
    53  			ekaclike.RTypeFloat32, ekaclike.RTypeFloat64,
    54  			ekaclike.RTypeComplex64, ekaclike.RTypeComplex128:
    55  			return "0"
    56  		}
    57  		switch reflect2.TypeOf(ekaclike.Interface{Type: rtype}.Pack()).Kind() {
    58  		case reflect.Array, reflect.Slice:
    59  			return "[]"
    60  		case reflect.Struct, reflect.Map:
    61  			return "{}"
    62  		default:
    63  			return ""
    64  		}
    65  	}
    66  
    67  	switch {
    68  
    69  	case rtype == 0:
    70  		return ""
    71  
    72  	case rtype == ekaclike.RTypeString:
    73  		return *(*string)(word)
    74  
    75  	case rtype == ekaclike.RTypeBytes:
    76  		return B2S(*(*[]byte)(word))
    77  
    78  	case rtype == ekaclike.RTypeBool:
    79  		if *(*bool)(word) {
    80  			return "true"
    81  		} else {
    82  			return "false"
    83  		}
    84  
    85  	case rtype == ekaclike.RTypeInt:
    86  		return strconv.FormatInt(int64(*(*int)(word)), 10)
    87  	case rtype == ekaclike.RTypeInt8:
    88  		return strconv.FormatInt(int64(*(*int8)(word)), 10)
    89  	case rtype == ekaclike.RTypeInt16:
    90  		return strconv.FormatInt(int64(*(*int16)(word)), 10)
    91  	case rtype == ekaclike.RTypeInt32:
    92  		return strconv.FormatInt(int64(*(*int32)(word)), 10)
    93  	case rtype == ekaclike.RTypeInt64:
    94  		return strconv.FormatInt(*(*int64)(word), 10)
    95  
    96  	case rtype == ekaclike.RTypeUint:
    97  		return strconv.FormatUint(uint64(*(*uint)(word)), 10)
    98  	case rtype == ekaclike.RTypeUint8:
    99  		return strconv.FormatUint(uint64(*(*uint8)(word)), 10)
   100  	case rtype == ekaclike.RTypeUint16:
   101  		return strconv.FormatUint(uint64(*(*uint16)(word)), 10)
   102  	case rtype == ekaclike.RTypeUint32:
   103  		return strconv.FormatUint(uint64(*(*uint32)(word)), 10)
   104  	case rtype == ekaclike.RTypeUint64:
   105  		return strconv.FormatUint(*(*uint64)(word), 10)
   106  
   107  	case rtype == ekaclike.RTypeFloat32:
   108  		return strconv.FormatFloat(float64(*(*float32)(word)), 'f', 2, 32)
   109  	case rtype == ekaclike.RTypeFloat64:
   110  		return strconv.FormatFloat(*(*float64)(word), 'f', 2, 64)
   111  
   112  	case rtype == ekaclike.RTypeComplex64:
   113  		return strconv.FormatComplex(complex128(*(*complex64)(word)), 'f', 2, 64)
   114  	case rtype == ekaclike.RTypeComplex128:
   115  		return strconv.FormatComplex(*(*complex128)(word), 'f', 2, 128)
   116  	}
   117  
   118  	// All standard types are over.
   119  	// Next types will be complex.
   120  
   121  	var (
   122  		eface = ekaclike.Interface{Type: rtype, Word: word}.Pack()
   123  		typ   = reflect2.TypeOf(eface)
   124  		kind  = typ.Kind()
   125  	)
   126  
   127  	switch {
   128  	case kind == reflect.Array && mask&TO_S_HANDLE_ARRAYS == 0:
   129  		return ""
   130  	case kind == reflect.Slice && mask&TO_S_HANDLE_ARRAYS == 0:
   131  		return ""
   132  	case kind == reflect.Struct && mask&TO_S_HANDLE_STRUCTS == 0:
   133  		return ""
   134  	case kind == reflect.Map && mask&TO_S_HANDLE_MAPS == 0:
   135  		return ""
   136  	case kind == reflect.Ptr && mask&TO_S_DEREFERENCE_PTR == 0:
   137  		return ""
   138  	}
   139  
   140  	// Well, it's complex case and it must be handled.
   141  
   142  	// TODO: Support fmt.Stringer interface.
   143  	// TODO: Separate handle all complex cases more optimised way.
   144  	return fmt.Sprintf("%+v", eface)
   145  }