From 384715cf9a39cd00d4abb283224b3baf9f6a3630 Mon Sep 17 00:00:00 2001 From: Stefan Benz Date: Thu, 24 Sep 2020 13:03:50 +0200 Subject: [PATCH] fix: added covered type of array and type of type --- pkg/{pack/package.go => code/code.go} | 316 +++++++++++++++++--------- pkg/docu/docu.go | 5 +- pkg/modules/modules.go | 104 +++++++-- pkg/modules/pack/package.go | 58 +++++ 4 files changed, 353 insertions(+), 130 deletions(-) rename pkg/{pack/package.go => code/code.go} (54%) create mode 100644 pkg/modules/pack/package.go diff --git a/pkg/pack/package.go b/pkg/code/code.go similarity index 54% rename from pkg/pack/package.go rename to pkg/code/code.go index fbe72aa..bc15945 100644 --- a/pkg/pack/package.go +++ b/pkg/code/code.go @@ -1,139 +1,72 @@ -package pack +package code import ( "github.com/caos/documentation/pkg/modules" + "github.com/caos/documentation/pkg/modules/pack" "github.com/caos/documentation/pkg/object" "github.com/caos/documentation/pkg/treeelement" "go/ast" "go/parser" "go/token" "io/ioutil" - "os" "path/filepath" "strings" ) -type Package struct { - BasePath string - Modules *modules.Modules - Files []string +var basicTypes []string = []string{ + "string", + "bool", + "int8", + "uint8", + "int16", + "uint16", + "int32", + "uint32", + "int64", + "uint64", + "int", + "uint", + "uintptr", + "byte", + "rune", } -func New(path string) *Package { - basePath := os.ExpandEnv(path) - - return &Package{ - BasePath: basePath, - Modules: modules.New(basePath), - } +func GetElementForStruct(packagePath string, structName string) (*treeelement.TreeElement, error) { + p := modules.CachedModule(packagePath).CachePackage(packagePath) + return recursiveGetElementForStruct(p, structName, nil) } -func (p *Package) GetGoFileList() []string { - files, err := getFilesInDirectory(p.BasePath) - if err != nil { - return nil - } - - goFiles := make([]string, 0) - for _, file := range files { - if !strings.HasSuffix(file, ".go") { - continue - } - goFiles = append(goFiles, file) - } - return goFiles -} - -func parseFile(path string) ([]byte, *ast.File, error) { - src, err := ioutil.ReadFile(path) - if err != nil { - return nil, nil, err - } - - fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "demo", src, parser.ParseComments) - if err != nil { - return nil, nil, err - } - - return src, file, nil -} - -func getImports(file *ast.File) map[string]string { - imports := make(map[string]string, 0) - for _, imp := range file.Imports { - name := filepath.Base(strings.TrimPrefix(strings.TrimSuffix(imp.Path.Value, "\""), "\"")) - if imp.Name != nil && imp.Name.Name != "" { - name = imp.Name.Name - } - imports[name] = strings.TrimPrefix(strings.TrimSuffix(imp.Path.Value, "\""), "\"") - } - return imports -} - -func getVariableFromField(src []byte, field *ast.Field) (varName string, impName string, typeName string, pointer bool, slice bool, mp bool, mapKeyType string) { - fieldInSource := string(src[field.Pos()-1 : field.End()-1]) - - parts := strings.Split(fieldInSource, " ") - copyParts := make([]string, 0) - for _, part := range parts { - if part != "" { - copyParts = append(copyParts, part) +func recursiveGetElementForStruct(p *pack.Package, structName string, obj *object.Object) (*treeelement.TreeElement, error) { + for _, basic := range basicTypes { + if basic == structName { + return nil, nil } } - parts = copyParts - - varName = parts[0] - inlineType := parts[1] - if strings.HasPrefix(inlineType, "map") { - inlineType = strings.TrimPrefix(inlineType, "map") - mp = true - typeParts := strings.SplitAfter(inlineType, "]") - mapKeyType = strings.TrimPrefix(strings.TrimSuffix(typeParts[0], "]"), "[") - inlineType = strings.TrimPrefix(inlineType, typeParts[0]) - } - if strings.HasPrefix(inlineType, "[]") { - inlineType = strings.TrimPrefix(inlineType, "[]") - slice = true + treeElement, cached := p.CachedElements[structName] + if cached { + retTreeElement := objectToElement(obj, treeElement.GoType) + retTreeElement.SubElements = treeElement.SubElements + return retTreeElement, nil } - if strings.HasPrefix(inlineType, "*") { - inlineType = strings.TrimPrefix(inlineType, "*") - pointer = true - } - - typeParts := strings.Split(inlineType, ".") - if len(typeParts) > 1 { - impName = typeParts[0] - typeName = typeParts[1] - } else { - typeName = typeParts[0] - } - return -} - -func (p *Package) GetElementForStruct(structName string) (*treeelement.TreeElement, error) { - return p.recursiveGetElementForStruct(structName, nil) -} - -func (p *Package) recursiveGetElementForStruct(structName string, obj *object.Object) (*treeelement.TreeElement, error) { goFiles := p.GetGoFileList() for _, path := range goFiles { - treeElement, err := getElementForStructInFile(path, structName, obj, p.Modules) + treeElement, err := getElementForStructInFile(path, structName, obj) if err != nil { return nil, err } if treeElement != nil { + p.CachedElements[structName] = treeElement return treeElement, nil } } return nil, nil } -func getElementForStructInFile(path string, structName string, obj *object.Object, modules *modules.Modules) (*treeelement.TreeElement, error) { +func getElementForStructInFile(path string, structName string, obj *object.Object) (*treeelement.TreeElement, error) { src, file, err := parseFile(path) if err != nil { return nil, err @@ -164,7 +97,9 @@ func getElementForStructInFile(path string, structName string, obj *object.Objec } // when struct s, oks := t.Type.(*ast.StructType) - if !oks { + a, oka := t.Type.(*ast.ArrayType) + sel, oksel := t.Type.(*ast.SelectorExpr) + if !oks && !oka && !oksel { return true } @@ -179,6 +114,108 @@ func getElementForStructInFile(path string, structName string, obj *object.Objec } } + // if type of a type + if sel != nil { + fieldObj := &object.Object{} + identX := sel.X.(*ast.Ident) + + fieldObj.Fieldname = structName + + importPath := modules.CachedModule(path).GetPathForImport(imports[identX.Name]) + fieldObj.PackageName = filepath.Base(importPath) + subElement, err := recursiveGetElementForStruct(modules.CachedModule(importPath).CachePackage(importPath), sel.Sel.Name, fieldObj) + if err != nil { + return false + } + if subElement != nil { + if subElement.SubElements != nil { + element.SubElements = append(element.SubElements, subElement.SubElements...) + } + } + return true + } + + // if array type + if a != nil { + fieldObj := &object.Object{} + sel, oksel := a.Elt.(*ast.SelectorExpr) + i, oki := a.Elt.(*ast.Ident) + + if oksel { + identX := sel.X.(*ast.Ident) + + fieldObj.Fieldname = structName + fieldObj.Collection = true + + importPath := modules.CachedModule(path).GetPathForImport(imports[identX.Name]) + fieldObj.PackageName = filepath.Base(importPath) + subElement, err := recursiveGetElementForStruct(modules.CachedModule(importPath).CachePackage(importPath), sel.Sel.Name, fieldObj) + if err != nil { + return false + } + if subElement != nil { + if subElement.SubElements != nil { + element.SubElements = append(element.SubElements, subElement.SubElements...) + } + } + return true + } + + if oki { + ty := i.Obj.Decl.(*ast.TypeSpec) + strc := ty.Type.(*ast.StructType) + + for _, field := range strc.Fields.List { + fieldObj := &object.Object{} + if field.Doc != nil && field.Doc.Text() != "" { + fieldObj.Comments = field.Doc.Text() + } + if field.Tag != nil && field.Tag.Value != "" { + fieldObj.Tag = field.Tag.Value + } + + v, i, t, _, c, m, mkey := getVariableFromField(src, field) + fieldObj.Fieldname = v + fieldObj.Collection = c + fieldObj.Mapkey = mkey + fieldObj.MapType = m + + if i != "" { + importPath := modules.CachedModule(path).GetPathForImport(imports[i]) + fieldObj.PackageName = filepath.Base(importPath) + subElement, err := recursiveGetElementForStruct(modules.CachedModule(importPath).CachePackage(importPath), t, fieldObj) + if err != nil { + return false + } + if subElement != nil { + if subElement.Inline { + if subElement.SubElements != nil { + element.SubElements = append(element.SubElements, subElement.SubElements...) + } + } else { + element.SubElements = append(element.SubElements, subElement) + } + } + } else { + dir := filepath.Dir(path) + subElement, err := recursiveGetElementForStruct(modules.CachedModule(dir).CachePackage(dir), t, fieldObj) + if err != nil { + return false + } + if subElement != nil { + // another struct type + element.SubElements = append(element.SubElements, subElement) + } else { + // basic types + element.SubElements = append(element.SubElements, objectToElement(fieldObj, t)) + } + } + } + return true + } + } + + //if struct type for _, field := range s.Fields.List { fieldObj := &object.Object{} if field.Doc != nil && field.Doc.Text() != "" { @@ -195,9 +232,9 @@ func getElementForStructInFile(path string, structName string, obj *object.Objec fieldObj.MapType = m if i != "" { - importPath := modules.GetPathForImport(imports[i]) + importPath := modules.CachedModule(path).GetPathForImport(imports[i]) fieldObj.PackageName = filepath.Base(importPath) - subElement, err := New(importPath).recursiveGetElementForStruct(t, fieldObj) + subElement, err := recursiveGetElementForStruct(modules.CachedModule(importPath).CachePackage(importPath), t, fieldObj) if err != nil { return false } @@ -211,13 +248,16 @@ func getElementForStructInFile(path string, structName string, obj *object.Objec } } } else { - subElement, err := New(filepath.Dir(path)).recursiveGetElementForStruct(t, fieldObj) + dir := filepath.Dir(path) + subElement, err := recursiveGetElementForStruct(modules.CachedModule(dir).CachePackage(dir), t, fieldObj) if err != nil { return false } if subElement != nil { + // another struct type element.SubElements = append(element.SubElements, subElement) } else { + // basic types element.SubElements = append(element.SubElements, objectToElement(fieldObj, t)) } } @@ -252,19 +292,71 @@ func objectToElement(obj *object.Object, ty string) *treeelement.TreeElement { return element } -func getFilesInDirectory(dirPath string) ([]string, error) { - files := make([]string, 0) +func parseFile(path string) ([]byte, *ast.File, error) { + src, err := ioutil.ReadFile(path) + if err != nil { + return nil, nil, err + } - infos, err := ioutil.ReadDir(dirPath) + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "demo", src, parser.ParseComments) if err != nil { - return nil, err + return nil, nil, err + } + + return src, file, nil +} + +func getImports(file *ast.File) map[string]string { + imports := make(map[string]string, 0) + for _, imp := range file.Imports { + name := filepath.Base(strings.TrimPrefix(strings.TrimSuffix(imp.Path.Value, "\""), "\"")) + if imp.Name != nil && imp.Name.Name != "" { + name = imp.Name.Name + } + imports[name] = strings.TrimPrefix(strings.TrimSuffix(imp.Path.Value, "\""), "\"") } + return imports +} - for _, info := range infos { - if !info.IsDir() { - files = append(files, filepath.Join(dirPath, info.Name())) +func getVariableFromField(src []byte, field *ast.Field) (varName string, impName string, typeName string, pointer bool, slice bool, mp bool, mapKeyType string) { + fieldInSource := string(src[field.Pos()-1 : field.End()-1]) + + parts := strings.Split(fieldInSource, " ") + copyParts := make([]string, 0) + for _, part := range parts { + if part != "" { + copyParts = append(copyParts, part) } } + parts = copyParts - return files, err + varName = parts[0] + inlineType := parts[1] + if strings.HasPrefix(inlineType, "map") { + inlineType = strings.TrimPrefix(inlineType, "map") + mp = true + typeParts := strings.SplitAfter(inlineType, "]") + mapKeyType = strings.TrimPrefix(strings.TrimSuffix(typeParts[0], "]"), "[") + inlineType = strings.TrimPrefix(inlineType, typeParts[0]) + } + + if strings.HasPrefix(inlineType, "[]") { + inlineType = strings.TrimPrefix(inlineType, "[]") + slice = true + } + + if strings.HasPrefix(inlineType, "*") { + inlineType = strings.TrimPrefix(inlineType, "*") + pointer = true + } + + typeParts := strings.Split(inlineType, ".") + if len(typeParts) > 1 { + impName = typeParts[0] + typeName = typeParts[1] + } else { + typeName = typeParts[0] + } + return } diff --git a/pkg/docu/docu.go b/pkg/docu/docu.go index bd3b9b2..6de1895 100644 --- a/pkg/docu/docu.go +++ b/pkg/docu/docu.go @@ -1,7 +1,7 @@ package docu import ( - "github.com/caos/documentation/pkg/pack" + "github.com/caos/documentation/pkg/code" "github.com/caos/documentation/pkg/treeelement" "io/ioutil" "os" @@ -21,8 +21,7 @@ func New() *Documentation { func (d *Documentation) Parse(path string, structName string) error { elements := make([]*treeelement.TreeElement, 0) - p := pack.New(path) - element, err := p.GetElementForStruct(structName) + element, err := code.GetElementForStruct(path, structName) if err != nil { return err } diff --git a/pkg/modules/modules.go b/pkg/modules/modules.go index 3d1524f..6c41060 100644 --- a/pkg/modules/modules.go +++ b/pkg/modules/modules.go @@ -1,46 +1,120 @@ package modules import ( + "github.com/caos/documentation/pkg/modules/pack" "os" "os/exec" "path/filepath" "strings" ) -type Modules struct { - basePath string +var cachedModules []*Module + +type Module struct { + basePath string + mod string + cachedImports map[string]string + cachedPackages map[string]*pack.Package } -func New(basePath string) *Modules { - path := strings.TrimPrefix(basePath, filepath.Join(os.ExpandEnv("$GOPATH"), "src")+"/") - return &Modules{ - basePath: getModuleForPath(path), +func New(basePath, mod string) *Module { + return &Module{ + basePath: basePath, + mod: mod, + cachedPackages: map[string]*pack.Package{}, + cachedImports: map[string]string{}, } } -func (m *Modules) GetPathForImport(importPath string) string { +func CachedModule(basePath string) *Module { + if cachedModules == nil { + cachedModules = make([]*Module, 0) + } + + baseRepoPrefix := filepath.Join(os.ExpandEnv("$GOPATH"), "src") + "/" + modPrefix := filepath.Join(os.ExpandEnv("$GOPATH"), "pkg", "mod") + "/" + + if strings.HasPrefix(basePath, baseRepoPrefix) { + path := strings.TrimPrefix(basePath, baseRepoPrefix) + mod, _ := getModuleForPath(path) + modPath := baseRepoPrefix + mod + + for _, module := range cachedModules { + if module.basePath == modPath { + return module + } + } + module := New(modPath, mod) + cachedModules = append(cachedModules, module) + return module + } + + mod, _ := getModuleForPath(basePath) + modPath := modPrefix + mod + + for _, module := range cachedModules { + if module.basePath == modPath { + return module + } + } + + module := New(modPath, mod) + cachedModules = append(cachedModules, module) + return module +} + +func (m *Module) CachePackage(path string) (ret *pack.Package) { + + localPath := strings.TrimPrefix(path, m.basePath) + + for _, cachedPackage := range m.cachedPackages { + if cachedPackage.ImportPath == localPath { + return cachedPackage + } + } + + ret = pack.New(path, localPath) + m.cachedPackages[localPath] = ret + return ret +} + +func (m *Module) GetPathForImport(importPath string) string { if importPath == "" { return "" } - if strings.HasPrefix(importPath, m.basePath) { - return filepath.Join(os.ExpandEnv("$GOPATH"), "src", importPath) + cached, found := m.cachedImports[importPath] + if found { + return cached + } + + resultPath := "" + if strings.HasPrefix(importPath, m.mod) { + resultPath = filepath.Join(os.ExpandEnv("$GOPATH"), "src", importPath) + } else { + mod, relativePath := getModuleForPath(importPath) + resultPath = filepath.Join(os.ExpandEnv("$GOPATH"), "pkg", "mod", mod, relativePath) } - return filepath.Join(os.ExpandEnv("$GOPATH"), "pkg", "mod", getModuleForPath(importPath)) + m.cachedImports[importPath] = resultPath + return resultPath +} + +func (m *Module) GetModulePath() string { + return m.basePath } -func getModuleForPath(path string) string { +func getModuleForPath(path string) (string, string) { levels := strings.Split(path, "/") for i := len(levels); i > 0; i-- { - path := filepath.Join(levels[0:i]...) - mod, err := checkGoList(path) + localPath := filepath.Join(levels[0:i]...) + mod, err := checkGoList(localPath) if err == nil { - return strings.Replace(mod, " ", "@", 1) + return strings.Replace(mod, " ", "@", 1), filepath.Join(levels[i:len(levels)]...) } } - return "" + return "", "" } func checkGoList(path string) (string, error) { diff --git a/pkg/modules/pack/package.go b/pkg/modules/pack/package.go new file mode 100644 index 0000000..c901e07 --- /dev/null +++ b/pkg/modules/pack/package.go @@ -0,0 +1,58 @@ +package pack + +import ( + "github.com/caos/documentation/pkg/treeelement" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +type Package struct { + BasePath string + ImportPath string + CachedElements map[string]*treeelement.TreeElement +} + +func New(basePath, importPath string) *Package { + path := os.ExpandEnv(basePath) + + return &Package{ + BasePath: path, + ImportPath: importPath, + CachedElements: map[string]*treeelement.TreeElement{}, + } +} + +func (p *Package) GetGoFileList() []string { + files, err := getFilesInDirectory(p.BasePath) + if err != nil { + return nil + } + + goFiles := make([]string, 0) + for _, file := range files { + if !strings.HasSuffix(file, ".go") { + continue + } + goFiles = append(goFiles, file) + } + return goFiles +} + +func getFilesInDirectory(dirPath string) ([]string, error) { + files := make([]string, 0) + + infos, err := ioutil.ReadDir(dirPath) + if err != nil { + return nil, err + } + + for _, info := range infos { + if !info.IsDir() { + files = append(files, filepath.Join(dirPath, info.Name())) + } + } + + return files, err +}