Skip to content

Instantly share code, notes, and snippets.

@tomoemon
Last active November 2, 2018 05:18
Show Gist options
  • Save tomoemon/f2281cccd5ffa589d72079816cf5b57c to your computer and use it in GitHub Desktop.
Save tomoemon/f2281cccd5ffa589d72079816cf5b57c to your computer and use it in GitHub Desktop.
任意の関数の情報を取得するための関数
import (
"reflect"
"regexp"
"runtime"
"strings"
"github.com/pkg/errors"
)
/*
runtime.FuncForPC(...).Name() は下記のような文字列を返す
fuga package にある fuga struct 経由で指定した場合
github.com/app/repo/fuga.(fuga).Hello-fm
# GetFuncInfo => {packageName: "fuga", parentName: "fuga", name: "Hello", isAnonymous: false}
fuga package にある fuga struct のポインタ経由で指定した場合
github.com/app/repo/fuga.(*fuga).Hello-fm
# GetFuncInfo => {packageName: "fuga", parentName: "fuga", name: "Hello", isAnonymous: false}
fuga package にある Fuga interface 経由で関数を指定した場合
github.com/app/repo/fuga.(Fuga).Hello-fm
# GetFuncInfo => {packageName: "fuga", parentName: "Fuga", name: "Hello", isAnonymous: false}
fuga package のトップレベル関数
github.com/app/repo/fuga.Hello
# GetFuncInfo => {packageName: "fuga", parentName: "", name: "Hello", isAnonymous: false}
fuga package のトップレベル関数内の無名関数
github.com/app/repo/fuga.Hello.func1
# GetFuncInfo => {packageName: "fuga", parentName: "", name: "Hello", isAnonymous: true}
fuga package にある fuga struct のポインタ経由で Hello 関数内にある無名関数
github.com/app/repo/fuga.(*fuga).Hello.func1
# GetFuncInfo => {packageName: "fuga", parentName: "fuga", name: "Hello", isAnonymous: true}
fuga package にある fuga struct のポインタ経由で Hello 関数内にある無名関数内の無名関数
github.com/app/repo/fuga.(*fuga).Hello.func2.1
# GetFuncInfo => {packageName: "fuga", parentName: "fuga", name: "Hello", isAnonymous: true}
*/
func IsFunc(v interface{}) bool {
return reflect.TypeOf(v).Kind() == reflect.Func
}
/*
関数を受け取って、その関数が属するパッケージ名、struct/interface名、関数名、無名関数かどうか、を返す
*/
func GetFuncInfo(f interface{}) FuncInfo {
if !IsFunc(f) {
panic(errors.Errorf("argument f must be a function GetFuncInfo: %#v", f))
}
fv := reflect.ValueOf(f)
fullName := runtime.FuncForPC(fv.Pointer()).Name()
splits := strings.Split(fullName, "/")
baseName := splits[len(splits)-1]
normalizePattern := regexp.MustCompile(`[^a-zA-Z.-]`)
plainName := normalizePattern.ReplaceAllString(baseName, "")
isMethod := strings.Index(baseName, "(") >= 0
names := strings.Split(plainName, ".")
if len(names) <= 1 {
panic(errors.Errorf("unexpected func name pattern GetFuncInfo: %+v", fullName))
}
packageName := names[0]
parentName := ""
name := ""
isAnonymous := false
if isMethod {
parentName = names[1]
name = removeHyphenSuffix(names[2])
if len(names) >= 4 {
isAnonymous = true
}
} else {
name = removeHyphenSuffix(names[1])
if len(names) >= 3 {
isAnonymous = true
}
}
return funcInfo{
fullName: fullName,
packageName: packageName,
parentName: parentName,
name: name,
isAnonymous: isAnonymous,
}
}
func removeHyphenSuffix(funcName string) string {
names := strings.Split(funcName, "-")
return names[0]
}
type funcInfo struct {
fullName string
packageName string
parentName string
name string
isAnonymous bool
}
func (f funcInfo) PackageName() string {
return f.packageName
}
func (f funcInfo) ParentName() string {
return f.parentName
}
func (f funcInfo) Name() string {
return f.name
}
func (f funcInfo) IsAnonymous() bool {
return f.isAnonymous
}
type FuncInfo interface {
PackageName() string
ParentName() string
Name() string
IsAnonymous() bool
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment