repogen/internal/code/extractor.go

189 lines
4.1 KiB
Go
Raw Normal View History

package code
import (
"fmt"
"go/ast"
"strconv"
"strings"
)
// ExtractComponents converts ast file into code components model
func ExtractComponents(f *ast.File) File {
var file File
file.PackageName = f.Name.Name
for _, decl := range f.Decls {
genDecl, ok := decl.(*ast.GenDecl)
if !ok {
continue
}
for _, spec := range genDecl.Specs {
2022-05-16 15:37:03 +00:00
switch spec := spec.(type) {
case *ast.ImportSpec:
var imp Import
2022-05-16 15:37:03 +00:00
if spec.Name != nil {
imp.Name = spec.Name.Name
}
2022-05-16 15:37:03 +00:00
importPath, err := strconv.Unquote(spec.Path.Value)
if err != nil {
2022-05-16 15:37:03 +00:00
fmt.Printf("cannot unquote import %s : %s \n", spec.Path.Value, err)
continue
}
imp.Path = importPath
file.Imports = append(file.Imports, imp)
2022-05-16 15:37:03 +00:00
case *ast.TypeSpec:
switch t := spec.Type.(type) {
2021-02-11 15:17:21 +00:00
case *ast.StructType:
2022-05-16 15:37:03 +00:00
file.Structs = append(file.Structs, extractStructType(spec.Name.Name, t))
2021-02-11 15:17:21 +00:00
case *ast.InterfaceType:
2022-05-16 15:37:03 +00:00
file.Interfaces = append(file.Interfaces, extractInterfaceType(spec.Name.Name, t))
}
2021-02-11 15:17:21 +00:00
}
}
}
return file
}
2021-02-11 15:17:21 +00:00
func extractStructType(name string, structType *ast.StructType) Struct {
str := Struct{
Name: name,
}
2021-02-11 15:17:21 +00:00
for _, field := range structType.Fields.List {
var strField StructField
for _, name := range field.Names {
strField.Name = name.Name
break
}
strField.Type = getType(field.Type)
if field.Tag != nil {
strField.Tags = extractStructTag(field.Tag.Value)
}
2021-02-11 15:17:21 +00:00
str.Fields = append(str.Fields, strField)
}
2021-02-11 15:17:21 +00:00
return str
}
2021-02-11 15:17:21 +00:00
func extractInterfaceType(name string, interfaceType *ast.InterfaceType) InterfaceType {
intf := InterfaceType{
Name: name,
}
2021-02-11 15:17:21 +00:00
for _, method := range interfaceType.Methods.List {
funcType, ok := method.Type.(*ast.FuncType)
if !ok {
continue
}
2021-02-11 15:17:21 +00:00
var name string
for _, n := range method.Names {
name = n.Name
break
}
2022-05-16 15:37:03 +00:00
var comments []string
if method.Doc != nil {
for _, comment := range method.Doc.List {
commentRunes := []rune(comment.Text)
commentText := strings.TrimSpace(string(commentRunes[2:]))
comments = append(comments, commentText)
}
}
meth := extractFunction(name, comments, funcType)
2021-02-11 15:17:21 +00:00
intf.Methods = append(intf.Methods, meth)
}
2021-02-11 15:17:21 +00:00
return intf
}
func extractStructTag(tagValue string) map[string][]string {
tagTokens := strings.Fields(tagValue[1 : len(tagValue)-1])
tags := make(map[string][]string)
for _, tagToken := range tagTokens {
colonIndex := strings.Index(tagToken, ":")
if colonIndex == -1 {
continue
}
tagKey := tagToken[:colonIndex]
tagValue, err := strconv.Unquote(tagToken[colonIndex+1:])
if err != nil {
fmt.Printf("cannot unquote struct tag %s : %s\n", tagToken[colonIndex+1:], err)
continue
}
tagValues := strings.Split(tagValue, ",")
tags[tagKey] = tagValues
}
return tags
}
2022-05-16 15:37:03 +00:00
func extractFunction(name string, comments []string, funcType *ast.FuncType) Method {
2021-02-01 14:39:20 +00:00
meth := Method{
2022-05-16 15:37:03 +00:00
Name: name,
Comments: comments,
2021-02-01 14:39:20 +00:00
}
for _, param := range funcType.Params.List {
paramType := getType(param.Type)
if len(param.Names) == 0 {
meth.Params = append(meth.Params, Param{Type: paramType})
continue
}
for _, name := range param.Names {
meth.Params = append(meth.Params, Param{
Name: name.Name,
Type: paramType,
})
}
}
if funcType.Results != nil {
for _, result := range funcType.Results.List {
meth.Returns = append(meth.Returns, getType(result.Type))
}
}
return meth
}
func getType(expr ast.Expr) Type {
2021-02-11 15:17:21 +00:00
switch expr := expr.(type) {
case *ast.Ident:
return SimpleType(expr.Name)
2021-02-11 15:17:21 +00:00
case *ast.SelectorExpr:
xExpr, ok := expr.X.(*ast.Ident)
if !ok {
2021-02-11 15:17:21 +00:00
return ExternalType{Name: expr.Sel.Name}
}
2021-02-11 15:17:21 +00:00
return ExternalType{PackageAlias: xExpr.Name, Name: expr.Sel.Name}
2021-02-11 15:17:21 +00:00
case *ast.StarExpr:
containedType := getType(expr.X)
return PointerType{ContainedType: containedType}
2021-02-11 15:17:21 +00:00
case *ast.ArrayType:
containedType := getType(expr.Elt)
return ArrayType{ContainedType: containedType}
case *ast.MapType:
keyType := getType(expr.Key)
valueType := getType(expr.Value)
return MapType{KeyType: keyType, ValueType: valueType}
2021-02-11 15:17:21 +00:00
case *ast.InterfaceType:
2022-05-16 15:37:03 +00:00
return extractInterfaceType("", expr)
2021-02-01 14:39:20 +00:00
}
return nil
}