blob: ace1dbe86e577fc525854f3e124c116d0b1ceaa2 [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001// 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.md file.
4
Pragya Arya324337e2020-02-20 14:35:08 +05305// Package function provides functionality for identifying function types.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07006package function
7
Pragya Arya324337e2020-02-20 14:35:08 +05308import (
9 "reflect"
10 "regexp"
11 "runtime"
12 "strings"
13)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070014
15type funcType int
16
17const (
18 _ funcType = iota
19
Pragya Arya324337e2020-02-20 14:35:08 +053020 tbFunc // func(T) bool
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070021 ttbFunc // func(T, T) bool
Pragya Arya324337e2020-02-20 14:35:08 +053022 trbFunc // func(T, R) bool
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070023 tibFunc // func(T, I) bool
24 trFunc // func(T) R
25
Pragya Arya324337e2020-02-20 14:35:08 +053026 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
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070033)
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 {
Pragya Arya324337e2020-02-20 14:35:08 +053044 case tbFunc: // func(T) bool
45 if ni == 1 && no == 1 && t.Out(0) == boolType {
46 return true
47 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070048 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 }
Pragya Arya324337e2020-02-20 14:35:08 +053052 case trbFunc: // func(T, R) bool
53 if ni == 2 && no == 1 && t.Out(0) == boolType {
54 return true
55 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070056 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}
Pragya Arya324337e2020-02-20 14:35:08 +053067
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}