func.go

  1// Copyright 2017, The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5// Package function provides functionality for identifying function types.
  6package function
  7
  8import (
  9	"reflect"
 10	"regexp"
 11	"runtime"
 12	"strings"
 13)
 14
 15type funcType int
 16
 17const (
 18	_ funcType = iota
 19
 20	tbFunc  // func(T) bool
 21	ttbFunc // func(T, T) bool
 22	ttiFunc // func(T, T) int
 23	trbFunc // func(T, R) bool
 24	tibFunc // func(T, I) bool
 25	trFunc  // func(T) R
 26
 27	Equal             = ttbFunc // func(T, T) bool
 28	EqualAssignable   = tibFunc // func(T, I) bool; encapsulates func(T, T) bool
 29	Transformer       = trFunc  // func(T) R
 30	ValueFilter       = ttbFunc // func(T, T) bool
 31	Less              = ttbFunc // func(T, T) bool
 32	Compare           = ttiFunc // func(T, T) int
 33	ValuePredicate    = tbFunc  // func(T) bool
 34	KeyValuePredicate = trbFunc // func(T, R) bool
 35)
 36
 37var boolType = reflect.TypeOf(true)
 38var intType = reflect.TypeOf(0)
 39
 40// IsType reports whether the reflect.Type is of the specified function type.
 41func IsType(t reflect.Type, ft funcType) bool {
 42	if t == nil || t.Kind() != reflect.Func || t.IsVariadic() {
 43		return false
 44	}
 45	ni, no := t.NumIn(), t.NumOut()
 46	switch ft {
 47	case tbFunc: // func(T) bool
 48		if ni == 1 && no == 1 && t.Out(0) == boolType {
 49			return true
 50		}
 51	case ttbFunc: // func(T, T) bool
 52		if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType {
 53			return true
 54		}
 55	case ttiFunc: // func(T, T) int
 56		if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == intType {
 57			return true
 58		}
 59	case trbFunc: // func(T, R) bool
 60		if ni == 2 && no == 1 && t.Out(0) == boolType {
 61			return true
 62		}
 63	case tibFunc: // func(T, I) bool
 64		if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType {
 65			return true
 66		}
 67	case trFunc: // func(T) R
 68		if ni == 1 && no == 1 {
 69			return true
 70		}
 71	}
 72	return false
 73}
 74
 75var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`)
 76
 77// NameOf returns the name of the function value.
 78func NameOf(v reflect.Value) string {
 79	fnc := runtime.FuncForPC(v.Pointer())
 80	if fnc == nil {
 81		return "<unknown>"
 82	}
 83	fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm"
 84
 85	// Method closures have a "-fm" suffix.
 86	fullName = strings.TrimSuffix(fullName, "-fm")
 87
 88	var name string
 89	for len(fullName) > 0 {
 90		inParen := strings.HasSuffix(fullName, ")")
 91		fullName = strings.TrimSuffix(fullName, ")")
 92
 93		s := lastIdentRx.FindString(fullName)
 94		if s == "" {
 95			break
 96		}
 97		name = s + "." + name
 98		fullName = strings.TrimSuffix(fullName, s)
 99
100		if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 {
101			fullName = fullName[:i]
102		}
103		fullName = strings.TrimSuffix(fullName, ".")
104	}
105	return strings.TrimSuffix(name, ".")
106}