Last active
November 2, 2018 05:18
-
-
Save tomoemon/f2281cccd5ffa589d72079816cf5b57c to your computer and use it in GitHub Desktop.
任意の関数の情報を取得するための関数
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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