我正在编写一个命令行应用程序,其中用户指定1)包含Go文件的目录,2)应该是http.Handler
的变量的名称,例如
go run cli.go /path/to/a/go/library MyCustomHandler
我正在尝试
http.Handler
我可以做前两个没问题 - 我调用parser.ParseDir
,然后将我想要的包作为*ast.Package
,然后像这样循环:
func findHttpHandler(pkg *ast.Package, handlerName string) (*ast.FuncDecl, error) {
for _, file := range pkg.Files {
for _, decl := range file.Decls {
gd, ok := decl.(*ast.GenDecl)
if !ok || gd.Tok != token.VAR {
continue
}
if len(gd.Specs) != 1 {
continue
}
spec0 := gd.Specs[0]
vs, ok := spec0.(*ast.ValueSpec)
if !ok {
continue
}
if len(vs.Names) != 1 {
continue
}
ident := vs.Names[0]
if ident.Name != handlerName {
continue
}
// ...
}
}
}
问题是此时ValueSpec.Type
为零,并且似乎无法确定这是否为http.Handler
。
go/types
包有更多用于检查类型的工具,但是看起来你需要做更多的设置工作来实现这一点,本质上是解析和类型检查整个程序。我是否需要走这条道路,或者是否有更简单的方法,只是使用ast
包,或以某种方式使用go build
?
答案 0 :(得分:1)
做了一些追踪并找到了方法,希望得到帮助
https://play.golang.org/p/f4XF8K_FbL
package main
import (
"go/parser"
"go/token"
"os"
"go/ast"
"log"
"net/http"
//"reflect"
)
func MyCustomHandler(w http.ResponseWriter, r* http.Request){
}
func findHttpHandler(pkg *ast.Package, handlerName string) (*ast.FuncDecl, error) {
for _, file := range pkg.Files {
for _, decl := range file.Decls {
fd, ok := decl.(*ast.FuncDecl)
if !ok || fd == nil{
continue
}
if fd.Name.Name != handlerName{
continue
}
if len(fd.Type.Params.List) == 2 {
p1 := fd.Type.Params.List[0]
p2 := fd.Type.Params.List[1]
exp, ok := p1.Type.(*ast.SelectorExpr)
if !ok{
break;
}
ident, ok := exp.X.(*ast.Ident)
if !ok{
break
}
if ident.Name!="http" || exp.Sel.Name != "ResponseWriter"{
break;
}
exp2, ok := p2.Type.(*ast.StarExpr)
if !ok{
break;
}
exp = exp2.X.(*ast.SelectorExpr)
ident, ok = exp.X.(*ast.Ident)
if !ok{
break
}
if ident.Name!="http" || exp.Sel.Name != "Request"{
break;
}
return fd, nil
}
}
}
return nil, nil
}
func main() {
fs := token.NewFileSet()
pkgs, err := parser.ParseDir(fs, os.Args[1], nil, parser.Trace)
if err != nil{
log.Fatalln(err)
}
for _,pkg:=range pkgs{
d, _ := findHttpHandler(pkg, "MyCustomHandler");
log.Println(d)
}
}