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