github.com/zeebo/goof@v0.0.0-20230907150950-e9457bc94477/reflect_helpers.go (about)

     1  package goof
     2  
     3  import (
     4  	"fmt"
     5  	"path"
     6  	"reflect"
     7  	"strings"
     8  	"unsafe"
     9  )
    10  
    11  func ifaces(values []reflect.Value) []interface{} {
    12  	out := make([]interface{}, 0, len(values))
    13  	for _, value := range values {
    14  		out = append(out, value.Interface())
    15  	}
    16  	return out
    17  }
    18  
    19  func reflectCanBeNil(rtyp reflect.Type) bool {
    20  	switch rtyp.Kind() {
    21  	case reflect.Interface,
    22  		reflect.Ptr,
    23  		reflect.Map,
    24  		reflect.Chan,
    25  		reflect.Slice:
    26  		return true
    27  	}
    28  	return false
    29  }
    30  
    31  func typesByString(types []reflect.Type) sortTypesByString {
    32  	cache := make([]string, 0, len(types))
    33  	for _, typ := range types {
    34  		cache = append(cache, typ.String())
    35  	}
    36  	return sortTypesByString{
    37  		types: types,
    38  		cache: cache,
    39  	}
    40  }
    41  
    42  type sortTypesByString struct {
    43  	types []reflect.Type
    44  	cache []string
    45  }
    46  
    47  func (s sortTypesByString) Len() int { return len(s.types) }
    48  
    49  func (s sortTypesByString) Less(i, j int) bool {
    50  	return s.cache[i] < s.cache[j]
    51  }
    52  
    53  func (s sortTypesByString) Swap(i, j int) {
    54  	s.cache[i], s.cache[j] = s.cache[j], s.cache[i]
    55  	s.types[i], s.types[j] = s.types[j], s.types[i]
    56  }
    57  
    58  var (
    59  	unsafePointerType = reflect.TypeOf((*unsafe.Pointer)(nil)).Elem()
    60  )
    61  
    62  // dwarfName does a best effort to return the dwarf entry name for the reflect
    63  // type so that we can map between them. here's hoping it doesn't do it wrong
    64  func dwarfName(rtyp reflect.Type) (out string) {
    65  	pkg_path := rtyp.PkgPath()
    66  	name := rtyp.Name()
    67  
    68  	switch {
    69  	// this type's PkgPath returns "" instead of "unsafe". hah.
    70  	case rtyp == unsafePointerType:
    71  		return "unsafe.Pointer"
    72  
    73  	case pkg_path != "":
    74  		// this is crazy, but sometimes a dot is encoded as %2e, but only when
    75  		// it's in the last path component. i wonder if this is sufficient.
    76  		if strings.Contains(pkg_path, "/") {
    77  			dir := path.Dir(pkg_path)
    78  			base := strings.Replace(path.Base(pkg_path), ".", "%2e", -1)
    79  			pkg_path = dir + "/" + base
    80  		}
    81  
    82  		return pkg_path + "." + name
    83  
    84  	case name != "":
    85  		return name
    86  
    87  	default:
    88  		switch rtyp.Kind() {
    89  		case reflect.Ptr:
    90  			return "*" + dwarfName(rtyp.Elem())
    91  
    92  		case reflect.Slice:
    93  			return "[]" + dwarfName(rtyp.Elem())
    94  
    95  		case reflect.Array:
    96  			return fmt.Sprintf("[%d]%s",
    97  				rtyp.Len(),
    98  				dwarfName(rtyp.Elem()))
    99  
   100  		case reflect.Map:
   101  			return fmt.Sprintf("map[%s]%s",
   102  				dwarfName(rtyp.Key()),
   103  				dwarfName(rtyp.Elem()))
   104  
   105  		case reflect.Chan:
   106  			prefix := "chan"
   107  			switch rtyp.ChanDir() {
   108  			case reflect.SendDir:
   109  				prefix = "chan<-"
   110  			case reflect.RecvDir:
   111  				prefix = "<-chan"
   112  			}
   113  			return fmt.Sprintf("%s %s",
   114  				prefix,
   115  				dwarfName(rtyp.Elem()))
   116  
   117  		// TODO: func, struct
   118  
   119  		default:
   120  			// oh well. this sometimes works.
   121  			return rtyp.String()
   122  		}
   123  	}
   124  }