111 lines
2.0 KiB
Go
111 lines
2.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"regexp"
|
|
)
|
|
|
|
var decorComment = regexp.MustCompile(`^//\s?kittenipc:api$`)
|
|
|
|
type apiStruct struct {
|
|
pkgName string
|
|
name string
|
|
methods []*ast.FuncDecl
|
|
}
|
|
|
|
type GoApiParser struct {
|
|
}
|
|
|
|
func (g *GoApiParser) Parse(sourceFile string) (Api, error) {
|
|
|
|
var apiStructs []*apiStruct
|
|
|
|
fileSet := token.NewFileSet()
|
|
astFile, err := parser.ParseFile(fileSet, sourceFile, nil, parser.ParseComments|parser.SkipObjectResolution)
|
|
if err != nil {
|
|
return Api{}, fmt.Errorf("parse file: %w", err)
|
|
}
|
|
|
|
pkgName := astFile.Name.Name
|
|
|
|
for _, decl := range astFile.Decls {
|
|
genDecl, ok := decl.(*ast.GenDecl)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if genDecl.Doc == nil {
|
|
continue
|
|
}
|
|
|
|
// use only last comment. https://tip.golang.org/doc/comment#syntax
|
|
lastComment := genDecl.Doc.List[len(genDecl.Doc.List)-1]
|
|
if !decorComment.MatchString(lastComment.Text) {
|
|
continue
|
|
}
|
|
|
|
typeSpec, ok := genDecl.Specs[0].(*ast.TypeSpec)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
structType, ok := typeSpec.Type.(*ast.StructType)
|
|
if !ok {
|
|
continue
|
|
}
|
|
_ = structType
|
|
|
|
apiStructs = append(apiStructs, &apiStruct{
|
|
name: typeSpec.Name.Name,
|
|
pkgName: pkgName,
|
|
})
|
|
}
|
|
|
|
if len(apiStructs) == 0 {
|
|
return Api{}, fmt.Errorf("no api struct found")
|
|
}
|
|
|
|
if len(apiStructs) > 1 {
|
|
return Api{}, fmt.Errorf("multiple api struct found")
|
|
}
|
|
|
|
for _, decl := range astFile.Decls {
|
|
funcDecl, ok := decl.(*ast.FuncDecl)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if !funcDecl.Name.IsExported() {
|
|
continue
|
|
}
|
|
|
|
if funcDecl.Recv == nil {
|
|
continue
|
|
}
|
|
|
|
reciever := funcDecl.Recv.List[0]
|
|
recvType := reciever.Type
|
|
|
|
star, isPointer := recvType.(*ast.StarExpr)
|
|
if isPointer {
|
|
recvType = star.X
|
|
}
|
|
|
|
recvIdent, ok := recvType.(*ast.Ident)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
for _, apiStrct := range apiStructs {
|
|
if recvIdent.Name == apiStrct.name && pkgName == apiStrct.pkgName {
|
|
apiStrct.methods = append(apiStrct.methods, funcDecl)
|
|
}
|
|
}
|
|
}
|
|
|
|
return Api{}, nil
|
|
}
|