func.go 2.5 KB

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