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}