github.com/haraldrudell/parl@v0.4.176/sets/e-string.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package sets
     7  
     8  import (
     9  	"fmt"
    10  	"reflect"
    11  
    12  	"github.com/haraldrudell/parl/internal/cyclebreaker"
    13  )
    14  
    15  // eString is an improved %v string converter.
    16  // E is a concrete type, that may be indirect, or interface
    17  //   - E can be:
    18  //   - — a type without String method
    19  //   - — a type with pointer-receiver String method
    20  //   - — a type with value-receiver String method
    21  //   - — a pointer to those
    22  //   - issues:
    23  //   - — if E is pointer to type without String method,
    24  //     a uintptr pointer is output ‘0x1400000e4b0’ where
    25  //     it is desirable to print values not pointers
    26  //   - — if E has pointer-receiver String but is not pointer,
    27  //     type assertion fails to find the String method
    28  //   - —
    29  //   - if e is basic type, %v: eString(1) → ‘1’
    30  //   - if e is *basic type, %v: eString(&1) → ‘1’
    31  //   - if e is *basic type, %v: eString(&abc) → ‘abc’
    32  //   - if e is String pointer receiver: eString((*) String()) → ‘string’
    33  //   - if e is *String pointer receiver: eString(&(*) String()) → ‘string’
    34  //   - if e is String value receiver: eString(() String()) → ‘string’
    35  //   - if e is *String value receiver: eString(&() String()) → ‘string’
    36  func eString[E any](e E) (s string) {
    37  
    38  	// this type assertion works if:
    39  	//	- e is any(struct) where struct has value-receiver String method
    40  	//	- e is any(&struct) where struct has value-receiver String method
    41  	//	- e is any(&struct) where struct has pointer-receiver String method
    42  	// this type assertion fails if:
    43  	//	- e is nil
    44  	//	- e does not have String method
    45  	//	- e has more than one indirection
    46  	//	- e is non-pointer struct with pointer-receiver String method
    47  	if stringer, ok := any(e).(fmt.Stringer); ok {
    48  		s = stringer.String()
    49  		return
    50  	}
    51  
    52  	// handle case where:
    53  	//	- e is non-pointer struct with pointer-receiver String method
    54  	//	- because e is concrete type, we can use address operator
    55  	//	- and then convert that to interface and do type assertion
    56  	if stringer, ok := any(&e).(fmt.Stringer); ok {
    57  		s = stringer.String()
    58  		return
    59  	}
    60  
    61  	// handle non-pointer types and nil
    62  	var isNil = cyclebreaker.IsNil(e)
    63  	var typeName string
    64  	var isPointer bool
    65  	if !isNil {
    66  		typeName = fmt.Sprintf("%T", e)
    67  		isPointer = typeName[0] == '*'
    68  	}
    69  	if isNil || !isPointer {
    70  		// some basic or composite type value or
    71  		//	- typed or untyped nil pointer: ‘<nil>’
    72  		s = fmt.Sprintf("%v", e)
    73  		return
    74  	}
    75  
    76  	// E is non-nil pointer to something
    77  	//	- it is not allowed to indirect E and
    78  	//	- it is not desirable to print using default %v: uintptr like ‘0x1400000e4c8’
    79  	//	- if the value is extracted using unsafe.Pointer,
    80  	//		%v will print whatever value the unsafe.Pointer is claimed to be
    81  	//	- since E is a real type and not any, reflection will work
    82  	//	- reflection can extract the pointed-to value and assign it to
    83  	//		an interface variable
    84  
    85  	// e is not nil and known to be pointer
    86  	//	- the concrete value stored in e
    87  	var reflectValue = reflect.ValueOf(e)
    88  	// extract the value E is pointing to
    89  	var pointedToValue = reflectValue.Elem()
    90  	// store the pointed-to value as interface value
    91  	var anyValue = pointedToValue.Interface()
    92  	// print anyValue’s dynamic type using %v
    93  	s = fmt.Sprintf("%v", anyValue)
    94  
    95  	return
    96  }