mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-03 01:43:15 +08:00
[v3] Improved parser for bound structs
This commit is contained in:
parent
bd184cab85
commit
e1279a054f
@ -5,7 +5,6 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v3/examples/binding/services"
|
"github.com/wailsapp/wails/v3/examples/binding/services"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,95 +1,96 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
import (
|
//
|
||||||
"bytes"
|
//import (
|
||||||
"fmt"
|
// "bytes"
|
||||||
"go/ast"
|
// "fmt"
|
||||||
"go/types"
|
// "go/ast"
|
||||||
"sort"
|
// "go/types"
|
||||||
"strings"
|
// "sort"
|
||||||
"unicode"
|
// "strings"
|
||||||
)
|
// "unicode"
|
||||||
|
//)
|
||||||
func GenerateModels(context *Context) ([]byte, error) {
|
//
|
||||||
var buf bytes.Buffer
|
//func GenerateModels(context *Context) ([]byte, error) {
|
||||||
var pkgs []Package
|
// var buf bytes.Buffer
|
||||||
specs := context.GetBoundStructs()
|
// var pkgs []Package
|
||||||
for pkg, pkgSpecs := range specs {
|
// specs := context.GetBoundStructs()
|
||||||
pkgs = append(pkgs, Package{Name: pkg, Specs: pkgSpecs})
|
// for pkg, pkgSpecs := range specs {
|
||||||
}
|
// pkgs = append(pkgs, Package{Name: pkg, Specs: pkgSpecs})
|
||||||
knownStructs := newAllModels(specs)
|
// }
|
||||||
sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].Name < pkgs[j].Name })
|
// knownStructs := newAllModels(specs)
|
||||||
for _, pkg := range pkgs {
|
// sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].Name < pkgs[j].Name })
|
||||||
if _, err := fmt.Fprintf(&buf, "namespace %s {\n", pkg.Name); err != nil {
|
// for _, pkg := range pkgs {
|
||||||
return nil, err
|
// if _, err := fmt.Fprintf(&buf, "namespace %s {\n", pkg.Name); err != nil {
|
||||||
}
|
// return nil, err
|
||||||
sort.Slice(pkg.Specs, func(i, j int) bool { return pkg.Specs[i].Name.Name < pkg.Specs[j].Name.Name })
|
// }
|
||||||
for _, spec := range pkg.Specs {
|
// sort.Slice(pkg.Specs, func(i, j int) bool { return pkg.Specs[i].Name.Name < pkg.Specs[j].Name.Name })
|
||||||
if structType, ok := spec.Type.(*ast.StructType); ok {
|
// for _, spec := range pkg.Specs {
|
||||||
if _, err := fmt.Fprintf(&buf, " class %s {\n", spec.Name.Name); err != nil {
|
// if structType, ok := spec.Type.(*ast.StructType); ok {
|
||||||
return nil, err
|
// if _, err := fmt.Fprintf(&buf, " class %s {\n", spec.Name.Name); err != nil {
|
||||||
}
|
// return nil, err
|
||||||
|
// }
|
||||||
for _, field := range structType.Fields.List {
|
//
|
||||||
|
// for _, field := range structType.Fields.List {
|
||||||
// Ignore field names that have a lower case first letter
|
//
|
||||||
if !unicode.IsUpper(rune(field.Names[0].Name[0])) {
|
// // Ignore field names that have a lower case first letter
|
||||||
continue
|
// if !unicode.IsUpper(rune(field.Names[0].Name[0])) {
|
||||||
}
|
// continue
|
||||||
|
// }
|
||||||
// Get the Go type of the field
|
//
|
||||||
goType := types.ExprString(field.Type)
|
// // Get the Go type of the field
|
||||||
// Check if the type is an array
|
// goType := types.ExprString(field.Type)
|
||||||
if arrayType, ok := field.Type.(*ast.ArrayType); ok {
|
// // Check if the type is an array
|
||||||
// Get the element type of the array
|
// if arrayType, ok := field.Type.(*ast.ArrayType); ok {
|
||||||
elementType := types.ExprString(arrayType.Elt)
|
// // Get the element type of the array
|
||||||
// Look up the corresponding TypeScript type
|
// elementType := types.ExprString(arrayType.Elt)
|
||||||
tsType, ok := goToTS[elementType]
|
// // Look up the corresponding TypeScript type
|
||||||
if !ok {
|
// tsType, ok := goToTS[elementType]
|
||||||
// strip off the * prefix if it is there
|
// if !ok {
|
||||||
if strings.HasPrefix(elementType, "*") {
|
// // strip off the * prefix if it is there
|
||||||
elementType = elementType[1:]
|
// if strings.HasPrefix(elementType, "*") {
|
||||||
}
|
// elementType = elementType[1:]
|
||||||
if knownStructs.exists(elementType) {
|
// }
|
||||||
tsType = elementType
|
// if knownStructs.exists(elementType) {
|
||||||
} else {
|
// tsType = elementType
|
||||||
tsType = "any"
|
// } else {
|
||||||
}
|
// tsType = "any"
|
||||||
}
|
// }
|
||||||
// Output the field as an array of the corresponding TypeScript type
|
// }
|
||||||
if _, err := fmt.Fprintf(&buf, " %s: %s[];\n", field.Names[0].Name, tsType); err != nil {
|
// // Output the field as an array of the corresponding TypeScript type
|
||||||
return nil, err
|
// if _, err := fmt.Fprintf(&buf, " %s: %s[];\n", field.Names[0].Name, tsType); err != nil {
|
||||||
}
|
// return nil, err
|
||||||
} else {
|
// }
|
||||||
// strip off the * prefix if it is there
|
// } else {
|
||||||
if strings.HasPrefix(goType, "*") {
|
// // strip off the * prefix if it is there
|
||||||
goType = goType[1:]
|
// if strings.HasPrefix(goType, "*") {
|
||||||
}
|
// goType = goType[1:]
|
||||||
// Look up the corresponding TypeScript type
|
// }
|
||||||
tsType, ok := goToTS[goType]
|
// // Look up the corresponding TypeScript type
|
||||||
if !ok {
|
// tsType, ok := goToTS[goType]
|
||||||
if knownStructs.exists(goType) {
|
// if !ok {
|
||||||
tsType = goType
|
// if knownStructs.exists(goType) {
|
||||||
} else {
|
// tsType = goType
|
||||||
tsType = "any"
|
// } else {
|
||||||
}
|
// tsType = "any"
|
||||||
}
|
// }
|
||||||
// Output the field as the corresponding TypeScript type
|
// }
|
||||||
if _, err := fmt.Fprintf(&buf, " %s: %s;\n", field.Names[0].Name, tsType); err != nil {
|
// // Output the field as the corresponding TypeScript type
|
||||||
return nil, err
|
// if _, err := fmt.Fprintf(&buf, " %s: %s;\n", field.Names[0].Name, tsType); err != nil {
|
||||||
}
|
// return nil, err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
if _, err := fmt.Fprintf(&buf, " }\n"); err != nil {
|
//
|
||||||
return nil, err
|
// if _, err := fmt.Fprintf(&buf, " }\n"); err != nil {
|
||||||
}
|
// return nil, err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
if _, err := fmt.Fprintf(&buf, "}\n\n"); err != nil {
|
//
|
||||||
return nil, err
|
// if _, err := fmt.Fprintf(&buf, "}\n\n"); err != nil {
|
||||||
}
|
// return nil, err
|
||||||
}
|
// }
|
||||||
return buf.Bytes(), nil
|
// }
|
||||||
}
|
// return buf.Bytes(), nil
|
||||||
|
//}
|
||||||
|
@ -3,14 +3,14 @@ package parser
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
|
|
||||||
"golang.org/x/tools/go/packages"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var Debug = false
|
var Debug = false
|
||||||
@ -21,24 +21,68 @@ func debug(msg string, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BoundStruct struct {
|
||||||
|
Name string
|
||||||
|
Methods map[string]*FuncSignature
|
||||||
|
Comments []string
|
||||||
|
}
|
||||||
|
|
||||||
type parsedPackage struct {
|
type parsedPackage struct {
|
||||||
name string
|
name string
|
||||||
pkg *ast.Package
|
pkg *ast.Package
|
||||||
boundStructs map[string]*ast.TypeSpec
|
boundStructs map[string]*BoundStruct
|
||||||
}
|
}
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
packages map[string]*parsedPackage
|
packages map[string]*parsedPackage
|
||||||
|
dir string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) GetBoundStructs() map[string][]*ast.TypeSpec {
|
func (c *Context) findImportPackage(pkgName string, pkg *ast.Package) (*ast.Package, error) {
|
||||||
structs := make(map[string][]*ast.TypeSpec)
|
for _, file := range pkg.Files {
|
||||||
for _, pkg := range c.packages {
|
for _, imp := range file.Imports {
|
||||||
for _, structType := range pkg.boundStructs {
|
path, err := strconv.Unquote(imp.Path.Value)
|
||||||
structs[pkg.name] = append(structs[pkg.name], structType)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if imp.Name != nil && imp.Name.Name == pkgName {
|
||||||
|
return c.getPackageFromPath(path)
|
||||||
|
} else {
|
||||||
|
_, pkgName := filepath.Split(path)
|
||||||
|
if pkgName == pkgName {
|
||||||
|
return c.getPackageFromPath(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return structs
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("package '%s' not found in %s", pkgName, pkg.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) getPackageFromPath(path string) (*ast.Package, error) {
|
||||||
|
dir, err := filepath.Abs(c.dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(path) {
|
||||||
|
dir = filepath.Join(dir, path)
|
||||||
|
} else {
|
||||||
|
impPkgDir, err := build.Import(path, dir, build.ImportMode(0))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dir = impPkgDir.Dir
|
||||||
|
}
|
||||||
|
impPkg, err := parser.ParseDir(token.NewFileSet(), dir, nil, parser.AllErrors)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for impName, impPkg := range impPkg {
|
||||||
|
if impName == "main" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return impPkg, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Package not found in imported package %s", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseDirectory(dir string) (*Context, error) {
|
func ParseDirectory(dir string) (*Context, error) {
|
||||||
@ -52,12 +96,13 @@ func ParseDirectory(dir string) (*Context, error) {
|
|||||||
dir = cwd
|
dir = cwd
|
||||||
}
|
}
|
||||||
println("Parsing directory " + dir)
|
println("Parsing directory " + dir)
|
||||||
pkgs, err := parser.ParseDir(fset, dir, nil, parser.AllErrors)
|
pkgs, err := parser.ParseDir(fset, dir, nil, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
context := &Context{
|
context := &Context{
|
||||||
|
dir: dir,
|
||||||
packages: make(map[string]*parsedPackage),
|
packages: make(map[string]*parsedPackage),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,16 +111,46 @@ func ParseDirectory(dir string) (*Context, error) {
|
|||||||
context.packages[pkg.Name] = &parsedPackage{
|
context.packages[pkg.Name] = &parsedPackage{
|
||||||
name: pkg.Name,
|
name: pkg.Name,
|
||||||
pkg: pkg,
|
pkg: pkg,
|
||||||
boundStructs: make(map[string]*ast.TypeSpec),
|
boundStructs: make(map[string]*BoundStruct),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findApplicationNewCalls(context)
|
findApplicationNewCalls(context)
|
||||||
|
err = findStructDefinitions(context)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return context, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findStructDefinitions(context *Context) error {
|
||||||
|
// iterate over the packages
|
||||||
|
for _, pkg := range context.packages {
|
||||||
|
// iterate the struct names
|
||||||
|
for structName, _ := range pkg.boundStructs {
|
||||||
|
structSpec, methods, comments := getStructTypeSpec(pkg.pkg, structName)
|
||||||
|
if structSpec == nil {
|
||||||
|
return fmt.Errorf("unable to find struct %s in package %s", structName, pkg.name)
|
||||||
|
}
|
||||||
|
pkg.boundStructs[structName] = &BoundStruct{
|
||||||
|
Name: structName,
|
||||||
|
Comments: comments,
|
||||||
|
}
|
||||||
|
if pkg.boundStructs[structName].Methods == nil {
|
||||||
|
pkg.boundStructs[structName].Methods = make(map[string]*FuncSignature)
|
||||||
|
}
|
||||||
|
for _, method := range methods {
|
||||||
|
pkg.boundStructs[structName].Methods[method.Name] = FuncTypeToSignature(method.Type)
|
||||||
|
pkg.boundStructs[structName].Methods[method.Name].Comments = method.Comments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func findApplicationNewCalls(context *Context) {
|
func findApplicationNewCalls(context *Context) {
|
||||||
|
println("Finding application.New calls")
|
||||||
// Iterate through the packages
|
// Iterate through the packages
|
||||||
currentPackages := lo.Keys(context.packages)
|
currentPackages := lo.Keys(context.packages)
|
||||||
|
|
||||||
@ -120,10 +195,10 @@ func findApplicationNewCalls(context *Context) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if selectorExpr.Sel.Name != "Application" {
|
if selectorExpr.Sel.Name != "Options" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if id, ok := selectorExpr.X.(*ast.Ident); !ok || id.Name != "options" {
|
if id, ok := selectorExpr.X.(*ast.Ident); !ok || id.Name != "application" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,15 +244,16 @@ func findApplicationNewCalls(context *Context) {
|
|||||||
ident, ok := boundStructLit.Type.(*ast.Ident)
|
ident, ok := boundStructLit.Type.(*ast.Ident)
|
||||||
if ok {
|
if ok {
|
||||||
if ident.Obj == nil {
|
if ident.Obj == nil {
|
||||||
structTypeSpec := findStructInPackage(thisPackage.pkg, ident.Name)
|
thisPackage.boundStructs[ident.Name] = &BoundStruct{
|
||||||
thisPackage.boundStructs[ident.Name] = structTypeSpec
|
Name: ident.Name,
|
||||||
findNestedStructs(structTypeSpec, file, packageName, context)
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Check if the ident is a struct type
|
// Check if the ident is a struct type
|
||||||
if t, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
if _, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
||||||
thisPackage.boundStructs[ident.Name] = t
|
thisPackage.boundStructs[ident.Name] = &BoundStruct{
|
||||||
findNestedStructs(t, file, packageName, context)
|
Name: ident.Name,
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Check the typespec decl is a struct
|
// Check the typespec decl is a struct
|
||||||
@ -189,343 +265,194 @@ func findApplicationNewCalls(context *Context) {
|
|||||||
// Check if the lit is a selector
|
// Check if the lit is a selector
|
||||||
selector, ok := boundStructLit.Type.(*ast.SelectorExpr)
|
selector, ok := boundStructLit.Type.(*ast.SelectorExpr)
|
||||||
if ok {
|
if ok {
|
||||||
getStructsFromSelector(selector, file, context)
|
// Check if the selector is an ident
|
||||||
continue
|
if ident, ok := selector.X.(*ast.Ident); ok {
|
||||||
}
|
// Check if the ident is a package
|
||||||
}
|
if _, ok := context.packages[ident.Name]; !ok {
|
||||||
}
|
externalPackage, err := context.getPackageFromPath(ident.Name)
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStructsFromSelector(selector *ast.SelectorExpr, file *ast.File, context *Context) {
|
|
||||||
debug("getStructsFromSelector called with selector '%s' on file '%s.go'", selector.Sel.Name, file.Name.Name)
|
|
||||||
|
|
||||||
// extract package name from selector
|
|
||||||
packageName := selector.X.(*ast.Ident).Name
|
|
||||||
|
|
||||||
if context.packages[packageName] == nil {
|
|
||||||
context.packages[packageName] = &parsedPackage{
|
|
||||||
name: packageName,
|
|
||||||
boundStructs: make(map[string]*ast.TypeSpec),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract struct name from selector
|
|
||||||
structName := selector.Sel.Name
|
|
||||||
|
|
||||||
// Find the package name from the imports
|
|
||||||
for _, imp := range file.Imports {
|
|
||||||
var match bool
|
|
||||||
if imp.Name == nil || imp.Name.Name == packageName {
|
|
||||||
match = true
|
|
||||||
}
|
|
||||||
if match == false {
|
|
||||||
pathSplit := strings.Split(imp.Path.Value, "/")
|
|
||||||
endPath := strings.Trim(pathSplit[len(pathSplit)-1], `"`)
|
|
||||||
match = endPath == packageName
|
|
||||||
}
|
|
||||||
|
|
||||||
if match {
|
|
||||||
// We have the import
|
|
||||||
cfg := &packages.Config{
|
|
||||||
Mode: packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedModule,
|
|
||||||
}
|
|
||||||
pkgs, err := packages.Load(cfg, strings.Trim(imp.Path.Value, `"`))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
println("Error getting package from path: " + err.Error())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
context.packages[ident.Name] = &parsedPackage{
|
||||||
|
name: ident.Name,
|
||||||
|
pkg: externalPackage,
|
||||||
|
boundStructs: make(map[string]*BoundStruct),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.packages[ident.Name].boundStructs[selector.Sel.Name] = &BoundStruct{
|
||||||
|
Name: selector.Sel.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
foundPackage := pkgs[0]
|
|
||||||
|
|
||||||
// Iterate the files in the package and find struct types
|
|
||||||
for _, parsedFile := range foundPackage.Syntax {
|
|
||||||
ast.Inspect(parsedFile, func(n ast.Node) bool {
|
|
||||||
if n == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch n.(type) {
|
|
||||||
case *ast.TypeSpec:
|
|
||||||
typeSpec := n.(*ast.TypeSpec)
|
|
||||||
if typeSpec.Name.Name == structName {
|
|
||||||
if _, ok := context.packages[packageName].boundStructs[structName]; !ok {
|
|
||||||
debug("Adding struct '%s' in package '%s'", structName, packageName)
|
|
||||||
context.packages[packageName].boundStructs[typeSpec.Name.Name] = typeSpec
|
|
||||||
findNestedStructs(typeSpec, parsedFile, packageName, context)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
//type Method struct {
|
||||||
|
// Name string
|
||||||
|
// Type *ast.FuncType
|
||||||
|
//}
|
||||||
|
|
||||||
func findNestedStructs(t *ast.TypeSpec, parsedFile *ast.File, pkgName string, context *Context) {
|
//func getStructTypeSpec(pkg *ast.Package, structName string) (*ast.TypeSpec, []Method) {
|
||||||
debug("findNestedStructs called with type '%s' on file '%s.go'", t.Name.Name, parsedFile.Name.Name)
|
// var typeSpec *ast.TypeSpec
|
||||||
structType, ok := t.Type.(*ast.StructType)
|
// var methods []Method
|
||||||
if !ok {
|
//
|
||||||
return
|
// // Iterate over all files in the package
|
||||||
}
|
// for _, file := range pkg.Files {
|
||||||
for _, field := range structType.Fields.List {
|
// // Iterate over all declarations in the file
|
||||||
for _, ident := range field.Names {
|
// for _, decl := range file.Decls {
|
||||||
switch t := ident.Obj.Decl.(*ast.Field).Type.(type) {
|
// // Check if the declaration is a type declaration
|
||||||
case *ast.Ident:
|
// if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.TYPE {
|
||||||
if t.Obj == nil {
|
// // Iterate over all type specifications in the type declaration
|
||||||
continue
|
// for _, spec := range genDecl.Specs {
|
||||||
}
|
// // Check if the type specification is a struct type specification
|
||||||
if t.Obj.Kind == ast.Typ {
|
// if tSpec, ok := spec.(*ast.TypeSpec); ok && tSpec.Name.Name == structName {
|
||||||
if _, ok := t.Obj.Decl.(*ast.TypeSpec); ok {
|
// // Check if the type specification is a struct type
|
||||||
if _, ok := context.packages[pkgName].boundStructs[t.Name]; !ok {
|
// if _, ok := tSpec.Type.(*ast.StructType); ok {
|
||||||
debug("Adding nested struct '%s' to package '%s'", t.Name, pkgName)
|
// typeSpec = tSpec
|
||||||
context.packages[pkgName].boundStructs[t.Name] = t.Obj.Decl.(*ast.TypeSpec)
|
// }
|
||||||
findNestedStructs(t.Obj.Decl.(*ast.TypeSpec), parsedFile, pkgName, context)
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// } else if funcDecl, ok := decl.(*ast.FuncDecl); ok && funcDecl.Recv != nil {
|
||||||
}
|
// // Check if the function has a receiver argument of the struct type
|
||||||
case *ast.SelectorExpr:
|
// recvType, ok := funcDecl.Recv.List[0].Type.(*ast.StarExpr)
|
||||||
if ident, ok := t.X.(*ast.Ident); ok {
|
// if ok {
|
||||||
if ident.IsExported() {
|
// if ident, ok := recvType.X.(*ast.Ident); ok && ident.Name == structName {
|
||||||
getStructsFromSelector(t, parsedFile, context)
|
// // Add the method to the list of methods
|
||||||
}
|
// method := Method{
|
||||||
}
|
// Name: funcDecl.Name.Name,
|
||||||
case *ast.StarExpr:
|
// Type: funcDecl.Type,
|
||||||
if sel, ok := t.X.(*ast.SelectorExpr); ok {
|
// }
|
||||||
if _, ok := sel.X.(*ast.Ident); ok {
|
// methods = append(methods, method)
|
||||||
if ident.IsExported() {
|
// }
|
||||||
getStructsFromSelector(sel, parsedFile, context)
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//
|
||||||
}
|
// return typeSpec, methods
|
||||||
}
|
//}
|
||||||
findStructsInMethods(t.Name.Name, parsedFile, pkgName, context)
|
|
||||||
|
|
||||||
}
|
type Arg struct {
|
||||||
|
|
||||||
func findStructsInMethods(name string, parsedFile *ast.File, pkgName string, context *Context) {
|
|
||||||
debug("findStructsInMethods called with type '%s' on file '%s.go'", name, parsedFile.Name.Name)
|
|
||||||
// Find the struct declaration for the given name
|
|
||||||
var structDecl *ast.TypeSpec
|
|
||||||
for _, decl := range parsedFile.Decls {
|
|
||||||
if fn, ok := decl.(*ast.FuncDecl); ok {
|
|
||||||
// check the receiver name is the same as the name given
|
|
||||||
if fn.Recv == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Check if the receiver is a pointer
|
|
||||||
if starExpr, ok := fn.Recv.List[0].Type.(*ast.StarExpr); ok {
|
|
||||||
if ident, ok := starExpr.X.(*ast.Ident); ok {
|
|
||||||
if ident.Name != name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ident, ok := fn.Recv.List[0].Type.(*ast.Ident); ok {
|
|
||||||
if ident.Name != name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
findStructsInMethodParams(fn, parsedFile, pkgName, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if structDecl == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Iterate the methods in the struct
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func findStructsInMethodParams(f *ast.FuncDecl, parsedFile *ast.File, pkgName string, context *Context) {
|
|
||||||
debug("findStructsInMethodParams called with type '%s' on file '%s.go'", f.Name.Name, parsedFile.Name.Name)
|
|
||||||
if f.Type.Params == nil {
|
|
||||||
for _, field := range f.Type.Params.List {
|
|
||||||
parseField(field, parsedFile, pkgName, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f.Type.Results != nil {
|
|
||||||
for _, field := range f.Type.Results.List {
|
|
||||||
parseField(field, parsedFile, pkgName, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseField(field *ast.Field, parsedFile *ast.File, pkgName string, context *Context) {
|
|
||||||
if se, ok := field.Type.(*ast.StarExpr); ok {
|
|
||||||
// Check if the star expr is a struct
|
|
||||||
if selExp, ok := se.X.(*ast.SelectorExpr); ok {
|
|
||||||
getStructsFromSelector(selExp, parsedFile, context)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ident, ok := se.X.(*ast.Ident); ok {
|
|
||||||
if ident.Obj == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ident.Obj.Kind == ast.Typ {
|
|
||||||
if _, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
|
||||||
if _, ok := context.packages[pkgName].boundStructs[ident.Name]; !ok {
|
|
||||||
debug("Adding field struct '%s' to package '%s'", ident.Name, pkgName)
|
|
||||||
context.packages[pkgName].boundStructs[ident.Name] = ident.Obj.Decl.(*ast.TypeSpec)
|
|
||||||
findNestedStructs(ident.Obj.Decl.(*ast.TypeSpec), parsedFile, pkgName, context)
|
|
||||||
} else {
|
|
||||||
debug("Struct %s already bound", ident.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if selExp, ok := field.Type.(*ast.SelectorExpr); ok {
|
|
||||||
getStructsFromSelector(selExp, parsedFile, context)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ident, ok := field.Type.(*ast.Ident); ok {
|
|
||||||
if ident.Obj == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ident.Obj.Kind == ast.Typ {
|
|
||||||
if _, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
|
||||||
if _, ok := context.packages[pkgName].boundStructs[ident.Name]; !ok {
|
|
||||||
debug("Adding field struct '%s' to package '%s'", ident.Name, pkgName)
|
|
||||||
context.packages[pkgName].boundStructs[ident.Name] = ident.Obj.Decl.(*ast.TypeSpec)
|
|
||||||
findNestedStructs(ident.Obj.Decl.(*ast.TypeSpec), parsedFile, pkgName, context)
|
|
||||||
} else {
|
|
||||||
debug("Struct %s already bound", ident.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func findStructInPackage(pkg *ast.Package, name string) *ast.TypeSpec {
|
|
||||||
for _, file := range pkg.Files {
|
|
||||||
for _, decl := range file.Decls {
|
|
||||||
if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.TYPE {
|
|
||||||
for _, spec := range gen.Specs {
|
|
||||||
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
|
||||||
if typeSpec.Name.Name == name {
|
|
||||||
if _, ok := typeSpec.Type.(*ast.StructType); ok {
|
|
||||||
return typeSpec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Package struct {
|
|
||||||
Name string
|
Name string
|
||||||
Specs []*ast.TypeSpec
|
Type string
|
||||||
}
|
}
|
||||||
|
|
||||||
var goToTS = map[string]string{
|
type FuncSignature struct {
|
||||||
"int": "number",
|
Comments []string
|
||||||
"int8": "number",
|
Inputs []Arg
|
||||||
"int16": "number",
|
Outputs []Arg
|
||||||
"int32": "number",
|
|
||||||
"int64": "number",
|
|
||||||
"uint": "number",
|
|
||||||
"uint8": "number",
|
|
||||||
"uint16": "number",
|
|
||||||
"uint32": "number",
|
|
||||||
"uint64": "number",
|
|
||||||
"float32": "number",
|
|
||||||
"float64": "number",
|
|
||||||
"string": "string",
|
|
||||||
"bool": "boolean",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//func GenerateModels(specs map[string][]*ast.TypeSpec) ([]byte, error) {
|
func FuncTypeToSignature(ft *ast.FuncType) *FuncSignature {
|
||||||
// var buf bytes.Buffer
|
sig := &FuncSignature{}
|
||||||
// var packages []Package
|
|
||||||
// for pkg, pkgSpecs := range specs {
|
|
||||||
// packages = append(packages, Package{Name: pkg, Specs: pkgSpecs})
|
|
||||||
// }
|
|
||||||
// sort.Slice(packages, func(i, j int) bool { return packages[i].Name < packages[j].Name })
|
|
||||||
// for _, pkg := range packages {
|
|
||||||
// if _, err := fmt.Fprintf(&buf, "namespace %s {\n", pkg.Name); err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// sort.Slice(pkg.Specs, func(i, j int) bool { return pkg.Specs[i].Name.Name < pkg.Specs[j].Name.Name })
|
|
||||||
// for _, spec := range pkg.Specs {
|
|
||||||
// if structType, ok := spec.Type.(*ast.StructType); ok {
|
|
||||||
// if _, err := fmt.Fprintf(&buf, " class %s {\n", spec.Name.Name); err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for _, field := range structType.Fields.List {
|
|
||||||
//
|
|
||||||
// // Get the Go type of the field
|
|
||||||
// goType := types.ExprString(field.Type)
|
|
||||||
// // Look up the corresponding TypeScript type
|
|
||||||
// tsType, ok := goToTS[goType]
|
|
||||||
// if !ok {
|
|
||||||
// tsType = goType
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if _, err := fmt.Fprintf(&buf, " %s: %s;\n", field.Names[0].Name, tsType); err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if _, err := fmt.Fprintf(&buf, " }\n"); err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// if _, err := fmt.Fprintf(&buf, " }\n"); err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if _, err := fmt.Fprintf(&buf, "}\n"); err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return buf.Bytes(), nil
|
|
||||||
//}
|
|
||||||
|
|
||||||
type allModels struct {
|
// process input arguments
|
||||||
known map[string]map[string]struct{}
|
if ft.Params != nil {
|
||||||
|
for _, field := range ft.Params.List {
|
||||||
|
arg := Arg{}
|
||||||
|
for _, name := range field.Names {
|
||||||
|
arg.Name = name.Name
|
||||||
|
}
|
||||||
|
arg.Type = tokenToString(field.Type)
|
||||||
|
sig.Inputs = append(sig.Inputs, arg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAllModels(models map[string][]*ast.TypeSpec) *allModels {
|
// process output arguments
|
||||||
result := &allModels{known: make(map[string]map[string]struct{})}
|
if ft.Results != nil {
|
||||||
// iterate over all models
|
for _, field := range ft.Results.List {
|
||||||
for pkg, pkgSpecs := range models {
|
arg := Arg{}
|
||||||
for _, spec := range pkgSpecs {
|
arg.Type = tokenToString(field.Type)
|
||||||
result.known[pkg] = make(map[string]struct{})
|
sig.Outputs = append(sig.Outputs, arg)
|
||||||
result.known[pkg][spec.Name.Name] = struct{}{}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *allModels) exists(name string) bool {
|
return sig
|
||||||
// Split the name into package and type
|
|
||||||
parts := strings.Split(name, ".")
|
|
||||||
typ := parts[0]
|
|
||||||
pkg := "main"
|
|
||||||
if len(parts) == 2 {
|
|
||||||
pkg = parts[0]
|
|
||||||
typ = parts[1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
knownPkg, ok := k.known[pkg]
|
func tokenToString(t ast.Expr) string {
|
||||||
if !ok {
|
switch t := t.(type) {
|
||||||
return false
|
case *ast.Ident:
|
||||||
|
return t.Name
|
||||||
|
case *ast.StarExpr:
|
||||||
|
return "*" + tokenToString(t.X)
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
return tokenToString(t.X) + "." + t.Sel.Name
|
||||||
|
case *ast.ArrayType:
|
||||||
|
return "[]" + tokenToString(t.Elt)
|
||||||
|
case *ast.StructType:
|
||||||
|
return "struct"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
_, ok = knownPkg[typ]
|
}
|
||||||
return ok
|
|
||||||
|
type Method struct {
|
||||||
|
Name string
|
||||||
|
Type *ast.FuncType
|
||||||
|
Comments []string // Add a field to capture comments for the method
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStructTypeSpec(pkg *ast.Package, structName string) (*ast.TypeSpec, []Method, []string) {
|
||||||
|
var typeSpec *ast.TypeSpec
|
||||||
|
var methods []Method
|
||||||
|
var structComments []string
|
||||||
|
|
||||||
|
// Iterate over all files in the package
|
||||||
|
for _, file := range pkg.Files {
|
||||||
|
// Iterate over all declarations in the file
|
||||||
|
for _, decl := range file.Decls {
|
||||||
|
// Check if the declaration is a type declaration
|
||||||
|
if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.TYPE {
|
||||||
|
// Iterate over all type specifications in the type declaration
|
||||||
|
for _, spec := range genDecl.Specs {
|
||||||
|
// Check if the type specification is a struct type specification
|
||||||
|
if tSpec, ok := spec.(*ast.TypeSpec); ok && tSpec.Name.Name == structName {
|
||||||
|
// Check if the type specification is a struct type
|
||||||
|
if _, ok := tSpec.Type.(*ast.StructType); ok {
|
||||||
|
// Get comments associated with the struct
|
||||||
|
if genDecl.Doc != nil {
|
||||||
|
for _, comment := range genDecl.Doc.List {
|
||||||
|
structComments = append(structComments, comment.Text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typeSpec = tSpec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if funcDecl, ok := decl.(*ast.FuncDecl); ok && funcDecl.Recv != nil {
|
||||||
|
// Check if the function has a receiver argument of the struct type
|
||||||
|
recvType, ok := funcDecl.Recv.List[0].Type.(*ast.StarExpr)
|
||||||
|
if ok {
|
||||||
|
if ident, ok := recvType.X.(*ast.Ident); ok && ident.Name == structName {
|
||||||
|
// Get comments associated with the method
|
||||||
|
if funcDecl.Doc != nil {
|
||||||
|
var comments []string
|
||||||
|
for _, comment := range funcDecl.Doc.List {
|
||||||
|
comments = append(comments, comment.Text)
|
||||||
|
}
|
||||||
|
// Add the method to the list of methods
|
||||||
|
method := Method{
|
||||||
|
Name: funcDecl.Name.Name,
|
||||||
|
Type: funcDecl.Type,
|
||||||
|
Comments: comments,
|
||||||
|
}
|
||||||
|
methods = append(methods, method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeSpec, methods, structComments
|
||||||
}
|
}
|
||||||
|
758
v3/internal/parser/parser_old.go
Normal file
758
v3/internal/parser/parser_old.go
Normal file
@ -0,0 +1,758 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
//
|
||||||
|
//import (
|
||||||
|
// "fmt"
|
||||||
|
// "go/ast"
|
||||||
|
// "go/build"
|
||||||
|
// "go/parser"
|
||||||
|
// "go/token"
|
||||||
|
// "os"
|
||||||
|
// "path/filepath"
|
||||||
|
// "strconv"
|
||||||
|
// "strings"
|
||||||
|
//
|
||||||
|
// "github.com/samber/lo"
|
||||||
|
//)
|
||||||
|
//
|
||||||
|
//var Debug = false
|
||||||
|
//
|
||||||
|
//func debug(msg string, args ...interface{}) {
|
||||||
|
// if Debug {
|
||||||
|
// println(fmt.Sprintf(msg, args...))
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//type BoundStruct struct {
|
||||||
|
// Name string
|
||||||
|
// MethodDecls []Method
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//type parsedPackage struct {
|
||||||
|
// name string
|
||||||
|
// pkg *ast.Package
|
||||||
|
// boundStructs map[string]*BoundStruct
|
||||||
|
// boundStructMethods map[string][]Method
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//type Context struct {
|
||||||
|
// packages map[string]*parsedPackage
|
||||||
|
// dir string
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (c *Context) findImportPackage(pkgName string, pkg *ast.Package) (*ast.Package, error) {
|
||||||
|
// for _, file := range pkg.Files {
|
||||||
|
// for _, imp := range file.Imports {
|
||||||
|
// path, err := strconv.Unquote(imp.Path.Value)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// if imp.Name != nil && imp.Name.Name == pkgName {
|
||||||
|
// return c.getPackageFromPath(path)
|
||||||
|
// } else {
|
||||||
|
// _, pkgName := filepath.Split(path)
|
||||||
|
// if pkgName == pkgName {
|
||||||
|
// return c.getPackageFromPath(path)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return nil, fmt.Errorf("Package %s not found in %s", pkgName, pkg.Name)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (c *Context) getPackageFromPath(path string) (*ast.Package, error) {
|
||||||
|
// dir, err := filepath.Abs(c.dir)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// if !filepath.IsAbs(path) {
|
||||||
|
// dir = filepath.Join(dir, path)
|
||||||
|
// } else {
|
||||||
|
// impPkgDir, err := build.Import(path, dir, build.ImportMode(0))
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// dir = impPkgDir.Dir
|
||||||
|
// }
|
||||||
|
// impPkg, err := parser.ParseDir(token.NewFileSet(), dir, nil, parser.AllErrors)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// for impName, impPkg := range impPkg {
|
||||||
|
// if impName == "main" {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// return impPkg, nil
|
||||||
|
// }
|
||||||
|
// return nil, fmt.Errorf("Package not found in imported package %s", path)
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func ParseDirectory(dir string) (*Context, error) {
|
||||||
|
// // Parse the directory
|
||||||
|
// fset := token.NewFileSet()
|
||||||
|
// if dir == "." || dir == "" {
|
||||||
|
// cwd, err := os.Getwd()
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// dir = cwd
|
||||||
|
// }
|
||||||
|
// println("Parsing directory " + dir)
|
||||||
|
// pkgs, err := parser.ParseDir(fset, dir, nil, parser.AllErrors)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// context := &Context{
|
||||||
|
// dir: dir,
|
||||||
|
// packages: make(map[string]*parsedPackage),
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Iterate through the packages
|
||||||
|
// for _, pkg := range pkgs {
|
||||||
|
// context.packages[pkg.Name] = &parsedPackage{
|
||||||
|
// name: pkg.Name,
|
||||||
|
// pkg: pkg,
|
||||||
|
// boundStructs: make(map[string]*BoundStruct),
|
||||||
|
// boundStructMethods: make(map[string][]Method),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// findApplicationNewCalls(context)
|
||||||
|
// err = findStructDefinitions(context)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return context, nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func findStructDefinitions(context *Context) error {
|
||||||
|
// // iterate over the packages
|
||||||
|
// for _, pkg := range context.packages {
|
||||||
|
// // iterate the struct names
|
||||||
|
// for structName, _ := range pkg.boundStructs {
|
||||||
|
// structSpec, methods := getStructTypeSpec(pkg.pkg, structName)
|
||||||
|
// if structSpec == nil {
|
||||||
|
// return fmt.Errorf("unable to find struct %s in package %s", structName, pkg.name)
|
||||||
|
// }
|
||||||
|
// pkg.boundStructs[structName] = &BoundStruct{
|
||||||
|
// Name: structName,
|
||||||
|
// MethodDecls: methods,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func findApplicationNewCalls(context *Context) {
|
||||||
|
// println("Finding application.New calls")
|
||||||
|
// // Iterate through the packages
|
||||||
|
// currentPackages := lo.Keys(context.packages)
|
||||||
|
//
|
||||||
|
// for _, packageName := range currentPackages {
|
||||||
|
// thisPackage := context.packages[packageName]
|
||||||
|
// debug("Parsing package: %s", packageName)
|
||||||
|
// // Iterate through the package's files
|
||||||
|
// for _, file := range thisPackage.pkg.Files {
|
||||||
|
// // Use an ast.Inspector to find the calls to application.New
|
||||||
|
// ast.Inspect(file, func(n ast.Node) bool {
|
||||||
|
// // Check if the node is a call expression
|
||||||
|
// callExpr, ok := n.(*ast.CallExpr)
|
||||||
|
// if !ok {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Check if the function being called is "application.New"
|
||||||
|
// selExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
|
||||||
|
// if !ok {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// if selExpr.Sel.Name != "New" {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// if id, ok := selExpr.X.(*ast.Ident); !ok || id.Name != "application" {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Check there is only 1 argument
|
||||||
|
// if len(callExpr.Args) != 1 {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Check argument 1 is a struct literal
|
||||||
|
// structLit, ok := callExpr.Args[0].(*ast.CompositeLit)
|
||||||
|
// if !ok {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Check struct literal is of type "application.Options"
|
||||||
|
// selectorExpr, ok := structLit.Type.(*ast.SelectorExpr)
|
||||||
|
// if !ok {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// if selectorExpr.Sel.Name != "Options" {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// if id, ok := selectorExpr.X.(*ast.Ident); !ok || id.Name != "application" {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for _, elt := range structLit.Elts {
|
||||||
|
// // Find the "Bind" field
|
||||||
|
// kvExpr, ok := elt.(*ast.KeyValueExpr)
|
||||||
|
// if !ok {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if id, ok := kvExpr.Key.(*ast.Ident); !ok || id.Name != "Bind" {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// // Check the value is a slice of interfaces
|
||||||
|
// sliceExpr, ok := kvExpr.Value.(*ast.CompositeLit)
|
||||||
|
// if !ok {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// var arrayType *ast.ArrayType
|
||||||
|
// if arrayType, ok = sliceExpr.Type.(*ast.ArrayType); !ok {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Check array type is of type "interface{}"
|
||||||
|
// if _, ok := arrayType.Elt.(*ast.InterfaceType); !ok {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// // Iterate through the slice elements
|
||||||
|
// for _, elt := range sliceExpr.Elts {
|
||||||
|
// // Check the element is a unary expression
|
||||||
|
// unaryExpr, ok := elt.(*ast.UnaryExpr)
|
||||||
|
// if ok {
|
||||||
|
// // Check the unary expression is a composite lit
|
||||||
|
// boundStructLit, ok := unaryExpr.X.(*ast.CompositeLit)
|
||||||
|
// if !ok {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// // Check if the composite lit is a struct
|
||||||
|
// if _, ok := boundStructLit.Type.(*ast.StructType); ok {
|
||||||
|
// // Parse struct
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// // Check if the lit is an ident
|
||||||
|
// ident, ok := boundStructLit.Type.(*ast.Ident)
|
||||||
|
// if ok {
|
||||||
|
// if ident.Obj == nil {
|
||||||
|
// thisPackage.boundStructs[ident.Name] = &BoundStruct{
|
||||||
|
// Name: ident.Name,
|
||||||
|
// }
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// // Check if the ident is a struct type
|
||||||
|
// if _, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
||||||
|
// thisPackage.boundStructs[ident.Name] = &BoundStruct{
|
||||||
|
// Name: ident.Name,
|
||||||
|
// }
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// // Check the typespec decl is a struct
|
||||||
|
// if _, ok := ident.Obj.Decl.(*ast.StructType); ok {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// // Check if the lit is a selector
|
||||||
|
// selector, ok := boundStructLit.Type.(*ast.SelectorExpr)
|
||||||
|
// if ok {
|
||||||
|
// // Check if the selector is an ident
|
||||||
|
// if ident, ok := selector.X.(*ast.Ident); ok {
|
||||||
|
// // Check if the ident is a package
|
||||||
|
// if _, ok := context.packages[ident.Name]; !ok {
|
||||||
|
// externalPackage, err := context.getPackageFromPath(ident.Name)
|
||||||
|
// if err != nil {
|
||||||
|
// println("Error getting package from path: " + err.Error())
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// context.packages[ident.Name] = &parsedPackage{
|
||||||
|
// name: ident.Name,
|
||||||
|
// pkg: externalPackage,
|
||||||
|
// boundStructs: make(map[string]*BoundStruct),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// context.packages[ident.Name].boundStructs[selector.Sel.Name] = &BoundStruct{
|
||||||
|
// Name: selector.Sel.Name,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return true
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//type Method struct {
|
||||||
|
// Name string
|
||||||
|
// Type *ast.FuncType
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
////func findApplicationNewCalls(context *Context) {
|
||||||
|
//// println("Finding application.New calls")
|
||||||
|
//// // Iterate through the packages
|
||||||
|
//// currentPackages := lo.Keys(context.packages)
|
||||||
|
////
|
||||||
|
//// for _, packageName := range currentPackages {
|
||||||
|
//// thisPackage := context.packages[packageName]
|
||||||
|
//// debug("Parsing package: %s", packageName)
|
||||||
|
//// // Iterate through the package's files
|
||||||
|
//// for _, file := range thisPackage.pkg.Files {
|
||||||
|
//// // Use an ast.Inspector to find the calls to application.New
|
||||||
|
//// ast.Inspect(file, func(n ast.Node) bool {
|
||||||
|
//// // Check if the node is a call expression
|
||||||
|
//// callExpr, ok := n.(*ast.CallExpr)
|
||||||
|
//// if !ok {
|
||||||
|
//// return true
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// // Check if the function being called is "application.New"
|
||||||
|
//// selExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
|
||||||
|
//// if !ok {
|
||||||
|
//// return true
|
||||||
|
//// }
|
||||||
|
//// if selExpr.Sel.Name != "New" {
|
||||||
|
//// return true
|
||||||
|
//// }
|
||||||
|
//// if id, ok := selExpr.X.(*ast.Ident); !ok || id.Name != "application" {
|
||||||
|
//// return true
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// // Check there is only 1 argument
|
||||||
|
//// if len(callExpr.Args) != 1 {
|
||||||
|
//// return true
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// // Check argument 1 is a struct literal
|
||||||
|
//// structLit, ok := callExpr.Args[0].(*ast.CompositeLit)
|
||||||
|
//// if !ok {
|
||||||
|
//// return true
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// // Check struct literal is of type "application.Options"
|
||||||
|
//// selectorExpr, ok := structLit.Type.(*ast.SelectorExpr)
|
||||||
|
//// if !ok {
|
||||||
|
//// return true
|
||||||
|
//// }
|
||||||
|
//// if selectorExpr.Sel.Name != "Application" {
|
||||||
|
//// return true
|
||||||
|
//// }
|
||||||
|
//// if id, ok := selectorExpr.X.(*ast.Ident); !ok || id.Name != "options" {
|
||||||
|
//// return true
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// for _, elt := range structLit.Elts {
|
||||||
|
//// // Find the "Bind" field
|
||||||
|
//// kvExpr, ok := elt.(*ast.KeyValueExpr)
|
||||||
|
//// if !ok {
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// if id, ok := kvExpr.Key.(*ast.Ident); !ok || id.Name != "Bind" {
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// // Check the value is a slice of interfaces
|
||||||
|
//// sliceExpr, ok := kvExpr.Value.(*ast.CompositeLit)
|
||||||
|
//// if !ok {
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// var arrayType *ast.ArrayType
|
||||||
|
//// if arrayType, ok = sliceExpr.Type.(*ast.ArrayType); !ok {
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// // Check array type is of type "interface{}"
|
||||||
|
//// if _, ok := arrayType.Elt.(*ast.InterfaceType); !ok {
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// // Iterate through the slice elements
|
||||||
|
//// for _, elt := range sliceExpr.Elts {
|
||||||
|
//// // Check the element is a unary expression
|
||||||
|
//// unaryExpr, ok := elt.(*ast.UnaryExpr)
|
||||||
|
//// if ok {
|
||||||
|
//// // Check the unary expression is a composite lit
|
||||||
|
//// boundStructLit, ok := unaryExpr.X.(*ast.CompositeLit)
|
||||||
|
//// if !ok {
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// // Check if the composite lit is a struct
|
||||||
|
//// if _, ok := boundStructLit.Type.(*ast.StructType); ok {
|
||||||
|
//// // Parse struct
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// // Check if the lit is an ident
|
||||||
|
//// ident, ok := boundStructLit.Type.(*ast.Ident)
|
||||||
|
//// if ok {
|
||||||
|
//// if ident.Obj == nil {
|
||||||
|
//// structTypeSpec := findStructInPackage(thisPackage.pkg, ident.Name)
|
||||||
|
//// thisPackage.boundStructs[ident.Name] = structTypeSpec
|
||||||
|
//// findNestedStructs(structTypeSpec, file, packageName, context)
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// // Check if the ident is a struct type
|
||||||
|
//// if t, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
||||||
|
//// thisPackage.boundStructs[ident.Name] = t
|
||||||
|
//// findNestedStructs(t, file, packageName, context)
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// // Check the typespec decl is a struct
|
||||||
|
//// if _, ok := ident.Obj.Decl.(*ast.StructType); ok {
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// }
|
||||||
|
//// // Check if the lit is a selector
|
||||||
|
//// selector, ok := boundStructLit.Type.(*ast.SelectorExpr)
|
||||||
|
//// if ok {
|
||||||
|
//// getStructsFromSelector(selector, file, context)
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// return true
|
||||||
|
//// })
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
//
|
||||||
|
////func getStructsFromSelector(selector *ast.SelectorExpr, file *ast.File, context *Context) {
|
||||||
|
//// debug("getStructsFromSelector called with selector '%s' on file '%s.go'", selector.Sel.Name, file.Name.Name)
|
||||||
|
////
|
||||||
|
//// // extract package name from selector
|
||||||
|
//// packageName := selector.X.(*ast.Ident).Name
|
||||||
|
////
|
||||||
|
//// if context.packages[packageName] == nil {
|
||||||
|
//// context.packages[packageName] = &parsedPackage{
|
||||||
|
//// name: packageName,
|
||||||
|
//// boundStructs: make(map[string]*BoundStruct),
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// // extract struct name from selector
|
||||||
|
//// structName := selector.Sel.Name
|
||||||
|
////
|
||||||
|
//// // Find the package name from the imports
|
||||||
|
//// for _, imp := range file.Imports {
|
||||||
|
//// var match bool
|
||||||
|
//// if imp.Name == nil || imp.Name.Name == packageName {
|
||||||
|
//// match = true
|
||||||
|
//// }
|
||||||
|
//// if match == false {
|
||||||
|
//// pathSplit := strings.Split(imp.Path.Value, "/")
|
||||||
|
//// endPath := strings.Trim(pathSplit[len(pathSplit)-1], `"`)
|
||||||
|
//// match = endPath == packageName
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// if match {
|
||||||
|
//// // We have the import
|
||||||
|
//// cfg := &packages.Config{
|
||||||
|
//// Mode: packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedModule,
|
||||||
|
//// }
|
||||||
|
//// pkgs, err := packages.Load(cfg, strings.Trim(imp.Path.Value, `"`))
|
||||||
|
//// if err != nil {
|
||||||
|
//// panic(err)
|
||||||
|
//// }
|
||||||
|
//// foundPackage := pkgs[0]
|
||||||
|
////
|
||||||
|
//// // Iterate the files in the package and find struct types
|
||||||
|
//// for _, parsedFile := range foundPackage.Syntax {
|
||||||
|
//// ast.Inspect(parsedFile, func(n ast.Node) bool {
|
||||||
|
//// if n == nil {
|
||||||
|
//// return false
|
||||||
|
//// }
|
||||||
|
//// switch n.(type) {
|
||||||
|
//// case *ast.TypeSpec:
|
||||||
|
//// typeSpec := n.(*ast.TypeSpec)
|
||||||
|
//// if typeSpec.Name.Name == structName {
|
||||||
|
//// if _, ok := context.packages[packageName].boundStructs[structName]; !ok {
|
||||||
|
//// debug("Adding struct '%s' in package '%s'", structName, packageName)
|
||||||
|
//// context.packages[packageName].boundStructs[typeSpec.Name.Name] = &BoundStruct{
|
||||||
|
//// Name: typeSpec.Name.Name,
|
||||||
|
//// }
|
||||||
|
//// findNestedStructs(typeSpec, parsedFile, packageName, context)
|
||||||
|
//// }
|
||||||
|
//// return false
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// return true
|
||||||
|
//// })
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
////}
|
||||||
|
////
|
||||||
|
////func findNestedStructs(t *ast.TypeSpec, parsedFile *ast.File, pkgName string, context *Context) {
|
||||||
|
//// debug("findNestedStructs called with type '%s' on file '%s.go'", t.Name.Name, parsedFile.Name.Name)
|
||||||
|
//// structType, ok := t.Type.(*ast.StructType)
|
||||||
|
//// if !ok {
|
||||||
|
//// return
|
||||||
|
//// }
|
||||||
|
//// for _, field := range structType.Fields.List {
|
||||||
|
//// for _, ident := range field.Names {
|
||||||
|
//// switch t := ident.Obj.Decl.(*ast.Field).Type.(type) {
|
||||||
|
//// case *ast.Ident:
|
||||||
|
//// if t.Obj == nil {
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// if t.Obj.Kind == ast.Typ {
|
||||||
|
//// if _, ok := t.Obj.Decl.(*ast.TypeSpec); ok {
|
||||||
|
//// if _, ok := context.packages[pkgName].boundStructs[t.Name]; !ok {
|
||||||
|
//// debug("Adding nested struct '%s' to package '%s'", t.Name, pkgName)
|
||||||
|
//// context.packages[pkgName].boundStructs[t.Name] = t.Obj.Decl.(*ast.TypeSpec)
|
||||||
|
//// findNestedStructs(t.Obj.Decl.(*ast.TypeSpec), parsedFile, pkgName, context)
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// case *ast.SelectorExpr:
|
||||||
|
//// if ident, ok := t.X.(*ast.Ident); ok {
|
||||||
|
//// if ident.IsExported() {
|
||||||
|
//// getStructsFromSelector(t, parsedFile, context)
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// case *ast.StarExpr:
|
||||||
|
//// if sel, ok := t.X.(*ast.SelectorExpr); ok {
|
||||||
|
//// if _, ok := sel.X.(*ast.Ident); ok {
|
||||||
|
//// if ident.IsExported() {
|
||||||
|
//// getStructsFromSelector(sel, parsedFile, context)
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// findStructsInMethods(t.Name.Name, parsedFile, pkgName, context)
|
||||||
|
////
|
||||||
|
////}
|
||||||
|
////
|
||||||
|
////func findStructsInMethods(name string, parsedFile *ast.File, pkgName string, context *Context) {
|
||||||
|
//// debug("findStructsInMethods called with type '%s' on file '%s.go'", name, parsedFile.Name.Name)
|
||||||
|
//// // Find the struct declaration for the given name
|
||||||
|
//// var structDecl *ast.TypeSpec
|
||||||
|
//// for _, decl := range parsedFile.Decls {
|
||||||
|
//// if fn, ok := decl.(*ast.FuncDecl); ok {
|
||||||
|
//// // check the receiver name is the same as the name given
|
||||||
|
//// if fn.Recv == nil {
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// // Check if the receiver is a pointer
|
||||||
|
//// if starExpr, ok := fn.Recv.List[0].Type.(*ast.StarExpr); ok {
|
||||||
|
//// if ident, ok := starExpr.X.(*ast.Ident); ok {
|
||||||
|
//// if ident.Name != name {
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// } else {
|
||||||
|
//// if ident, ok := fn.Recv.List[0].Type.(*ast.Ident); ok {
|
||||||
|
//// if ident.Name != name {
|
||||||
|
//// continue
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// findStructsInMethodParams(fn, parsedFile, pkgName, context)
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// if structDecl == nil {
|
||||||
|
//// return
|
||||||
|
//// }
|
||||||
|
//// // Iterate the methods in the struct
|
||||||
|
////
|
||||||
|
////}
|
||||||
|
////
|
||||||
|
////func findStructsInMethodParams(f *ast.FuncDecl, parsedFile *ast.File, pkgName string, context *Context) {
|
||||||
|
//// debug("findStructsInMethodParams called with type '%s' on file '%s.go'", f.Name.Name, parsedFile.Name.Name)
|
||||||
|
//// if f.Type.Params == nil {
|
||||||
|
//// for _, field := range f.Type.Params.List {
|
||||||
|
//// parseField(field, parsedFile, pkgName, context)
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// if f.Type.Results != nil {
|
||||||
|
//// for _, field := range f.Type.Results.List {
|
||||||
|
//// parseField(field, parsedFile, pkgName, context)
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
//
|
||||||
|
////func parseField(field *ast.Field, parsedFile *ast.File, pkgName string, context *Context) {
|
||||||
|
//// if se, ok := field.Type.(*ast.StarExpr); ok {
|
||||||
|
//// // Check if the star expr is a struct
|
||||||
|
//// if selExp, ok := se.X.(*ast.SelectorExpr); ok {
|
||||||
|
//// getStructsFromSelector(selExp, parsedFile, context)
|
||||||
|
//// return
|
||||||
|
//// }
|
||||||
|
//// if ident, ok := se.X.(*ast.Ident); ok {
|
||||||
|
//// if ident.Obj == nil {
|
||||||
|
//// return
|
||||||
|
//// }
|
||||||
|
//// if ident.Obj.Kind == ast.Typ {
|
||||||
|
//// if _, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
||||||
|
//// if _, ok := context.packages[pkgName].boundStructs[ident.Name]; !ok {
|
||||||
|
//// debug("Adding field struct '%s' to package '%s'", ident.Name, pkgName)
|
||||||
|
//// context.packages[pkgName].boundStructs[ident.Name] = ident.Obj.Decl.(*ast.TypeSpec)
|
||||||
|
//// findNestedStructs(ident.Obj.Decl.(*ast.TypeSpec), parsedFile, pkgName, context)
|
||||||
|
//// } else {
|
||||||
|
//// debug("Struct %s already bound", ident.Name)
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// if selExp, ok := field.Type.(*ast.SelectorExpr); ok {
|
||||||
|
//// getStructsFromSelector(selExp, parsedFile, context)
|
||||||
|
//// return
|
||||||
|
//// }
|
||||||
|
//// if ident, ok := field.Type.(*ast.Ident); ok {
|
||||||
|
//// if ident.Obj == nil {
|
||||||
|
//// return
|
||||||
|
//// }
|
||||||
|
//// if ident.Obj.Kind == ast.Typ {
|
||||||
|
//// if _, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
||||||
|
//// if _, ok := context.packages[pkgName].boundStructs[ident.Name]; !ok {
|
||||||
|
//// debug("Adding field struct '%s' to package '%s'", ident.Name, pkgName)
|
||||||
|
//// context.packages[pkgName].boundStructs[ident.Name] = ident.Obj.Decl.(*ast.TypeSpec)
|
||||||
|
//// findNestedStructs(ident.Obj.Decl.(*ast.TypeSpec), parsedFile, pkgName, context)
|
||||||
|
//// } else {
|
||||||
|
//// debug("Struct %s already bound", ident.Name)
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////
|
||||||
|
////func findStructInPackage(pkg *ast.Package, name string) *ast.TypeSpec {
|
||||||
|
//// for _, file := range pkg.Files {
|
||||||
|
//// for _, decl := range file.Decls {
|
||||||
|
//// if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.TYPE {
|
||||||
|
//// for _, spec := range gen.Specs {
|
||||||
|
//// if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
||||||
|
//// if typeSpec.Name.Name == name {
|
||||||
|
//// if _, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||||
|
//// return typeSpec
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// return nil
|
||||||
|
////}
|
||||||
|
////
|
||||||
|
////type Package struct {
|
||||||
|
//// Name string
|
||||||
|
//// Specs []*ast.TypeSpec
|
||||||
|
////}
|
||||||
|
////
|
||||||
|
////var goToTS = map[string]string{
|
||||||
|
//// "int": "number",
|
||||||
|
//// "int8": "number",
|
||||||
|
//// "int16": "number",
|
||||||
|
//// "int32": "number",
|
||||||
|
//// "int64": "number",
|
||||||
|
//// "uint": "number",
|
||||||
|
//// "uint8": "number",
|
||||||
|
//// "uint16": "number",
|
||||||
|
//// "uint32": "number",
|
||||||
|
//// "uint64": "number",
|
||||||
|
//// "float32": "number",
|
||||||
|
//// "float64": "number",
|
||||||
|
//// "string": "string",
|
||||||
|
//// "bool": "boolean",
|
||||||
|
////}
|
||||||
|
//
|
||||||
|
////func GenerateModels(specs map[string][]*ast.TypeSpec) ([]byte, error) {
|
||||||
|
//// var buf bytes.Buffer
|
||||||
|
//// var packages []Package
|
||||||
|
//// for pkg, pkgSpecs := range specs {
|
||||||
|
//// packages = append(packages, Package{Name: pkg, Specs: pkgSpecs})
|
||||||
|
//// }
|
||||||
|
//// sort.Slice(packages, func(i, j int) bool { return packages[i].Name < packages[j].Name })
|
||||||
|
//// for _, pkg := range packages {
|
||||||
|
//// if _, err := fmt.Fprintf(&buf, "namespace %s {\n", pkg.Name); err != nil {
|
||||||
|
//// return nil, err
|
||||||
|
//// }
|
||||||
|
//// sort.Slice(pkg.Specs, func(i, j int) bool { return pkg.Specs[i].Name.Name < pkg.Specs[j].Name.Name })
|
||||||
|
//// for _, spec := range pkg.Specs {
|
||||||
|
//// if structType, ok := spec.Type.(*ast.StructType); ok {
|
||||||
|
//// if _, err := fmt.Fprintf(&buf, " class %s {\n", spec.Name.Name); err != nil {
|
||||||
|
//// return nil, err
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// for _, field := range structType.Fields.List {
|
||||||
|
////
|
||||||
|
//// // Get the Go type of the field
|
||||||
|
//// goType := types.ExprString(field.Type)
|
||||||
|
//// // Look up the corresponding TypeScript type
|
||||||
|
//// tsType, ok := goToTS[goType]
|
||||||
|
//// if !ok {
|
||||||
|
//// tsType = goType
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// if _, err := fmt.Fprintf(&buf, " %s: %s;\n", field.Names[0].Name, tsType); err != nil {
|
||||||
|
//// return nil, err
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// if _, err := fmt.Fprintf(&buf, " }\n"); err != nil {
|
||||||
|
//// return nil, err
|
||||||
|
//// }
|
||||||
|
//// if _, err := fmt.Fprintf(&buf, " }\n"); err != nil {
|
||||||
|
//// return nil, err
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// if _, err := fmt.Fprintf(&buf, "}\n"); err != nil {
|
||||||
|
//// return nil, err
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// return buf.Bytes(), nil
|
||||||
|
////}
|
||||||
|
//
|
||||||
|
//type allModels struct {
|
||||||
|
// known map[string]map[string]struct{}
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func newAllModels(models map[string][]*ast.TypeSpec) *allModels {
|
||||||
|
// result := &allModels{known: make(map[string]map[string]struct{})}
|
||||||
|
// // iterate over all models
|
||||||
|
// for pkg, pkgSpecs := range models {
|
||||||
|
// for _, spec := range pkgSpecs {
|
||||||
|
// result.known[pkg] = make(map[string]struct{})
|
||||||
|
// result.known[pkg][spec.Name.Name] = struct{}{}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return result
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (k *allModels) exists(name string) bool {
|
||||||
|
// // Split the name into package and type
|
||||||
|
// parts := strings.Split(name, ".")
|
||||||
|
// typ := parts[0]
|
||||||
|
// pkg := "main"
|
||||||
|
// if len(parts) == 2 {
|
||||||
|
// pkg = parts[0]
|
||||||
|
// typ = parts[1]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// knownPkg, ok := k.known[pkg]
|
||||||
|
// if !ok {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// _, ok = knownPkg[typ]
|
||||||
|
// return ok
|
||||||
|
//}
|
@ -34,9 +34,9 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should find bound services from other packages",
|
name: "should find multiple bound services over multiple packages",
|
||||||
dir: "../../examples/binding",
|
dir: "testdata/struct_literal_multiple_other",
|
||||||
want: []string{"main.localStruct", "services.GreetService", "models.Person"},
|
want: []string{"main.GreetService", "services.OtherService"},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -50,7 +50,8 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for name, pkg := range got.packages {
|
for name, pkg := range got.packages {
|
||||||
for structName := range pkg.boundStructs {
|
for structName, structType := range pkg.boundStructs {
|
||||||
|
require.NotNil(t, structType)
|
||||||
require.True(t, lo.Contains(tt.want, name+"."+structName))
|
require.True(t, lo.Contains(tt.want, name+"."+structName))
|
||||||
tt.want = lo.Without(tt.want, name+"."+structName)
|
tt.want = lo.Without(tt.want, name+"."+structName)
|
||||||
}
|
}
|
||||||
@ -58,87 +59,88 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
require.Empty(t, tt.want)
|
require.Empty(t, tt.want)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateTypeScript(t *testing.T) {
|
//func TestGenerateTypeScript(t *testing.T) {
|
||||||
tests := []struct {
|
// tests := []struct {
|
||||||
name string
|
// name string
|
||||||
dir string
|
// dir string
|
||||||
want string
|
// want string
|
||||||
wantErr bool
|
// wantErr bool
|
||||||
}{
|
// }{
|
||||||
{
|
// {
|
||||||
name: "should find single bound service",
|
// name: "should find single bound service",
|
||||||
dir: "testdata/struct_literal_single",
|
// dir: "testdata/struct_literal_single",
|
||||||
want: `namespace main {
|
// want: `namespace main {
|
||||||
class GreetService {
|
// class GreetService {
|
||||||
SomeVariable: number;
|
// SomeVariable: number;
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
`,
|
//`,
|
||||||
wantErr: false,
|
// wantErr: false,
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "should find multiple bound services",
|
// name: "should find multiple bound services",
|
||||||
dir: "testdata/struct_literal_multiple",
|
// dir: "testdata/struct_literal_multiple",
|
||||||
want: `namespace main {
|
// want: `namespace main {
|
||||||
class GreetService {
|
// class GreetService {
|
||||||
SomeVariable: number;
|
// SomeVariable: number;
|
||||||
}
|
// }
|
||||||
class OtherService {
|
// class OtherService {
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
`,
|
//`,
|
||||||
wantErr: false,
|
// wantErr: false,
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "should find multiple bound services over multiple files",
|
// name: "should find multiple bound services over multiple files",
|
||||||
dir: "testdata/struct_literal_multiple_files",
|
// dir: "testdata/struct_literal_multiple_files",
|
||||||
want: `namespace main {
|
// want: `namespace main {
|
||||||
class GreetService {
|
// class GreetService {
|
||||||
SomeVariable: number;
|
// SomeVariable: number;
|
||||||
}
|
// }
|
||||||
class OtherService {
|
// class OtherService {
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
`,
|
//`,
|
||||||
wantErr: false,
|
// wantErr: false,
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "should find bound services from other packages",
|
// name: "should find bound services from other packages",
|
||||||
dir: "../../examples/binding",
|
// dir: "../../examples/binding",
|
||||||
want: `namespace main {
|
// want: `namespace main {
|
||||||
class localStruct {
|
// class localStruct {
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
namespace models {
|
//namespace models {
|
||||||
class Person {
|
// class Person {
|
||||||
Name: string;
|
// Name: string;
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
namespace services {
|
//namespace services {
|
||||||
class GreetService {
|
// class GreetService {
|
||||||
SomeVariable: number;
|
// SomeVariable: number;
|
||||||
Parent: models.Person;
|
// Parent: models.Person;
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
`,
|
//`,
|
||||||
wantErr: false,
|
// wantErr: false,
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
for _, tt := range tests {
|
// for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
// t.Run(tt.name, func(t *testing.T) {
|
||||||
Debug = true
|
// Debug = true
|
||||||
context, err := ParseDirectory(tt.dir)
|
// context, err := ParseDirectory(tt.dir)
|
||||||
if (err != nil) != tt.wantErr {
|
// if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
// t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
ts, err := GenerateModels(context)
|
// ts, err := GenerateModels(context)
|
||||||
require.NoError(t, err)
|
// require.NoError(t, err)
|
||||||
require.Equal(t, tt.want, string(ts))
|
// require.Equal(t, tt.want, string(ts))
|
||||||
|
//
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
38
v3/internal/parser/testdata/struct_literal_multiple_other/main.go
vendored
Normal file
38
v3/internal/parser/testdata/struct_literal_multiple_other/main.go
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GreetService is great
|
||||||
|
type GreetService struct {
|
||||||
|
SomeVariable int
|
||||||
|
lowerCase string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Greet does XYZ
|
||||||
|
func (*GreetService) Greet(name string) string {
|
||||||
|
return "Hello " + name
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := application.New(application.Options{
|
||||||
|
Bind: []interface{}{
|
||||||
|
&GreetService{},
|
||||||
|
&services.OtherService{},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
app.NewWebviewWindow()
|
||||||
|
|
||||||
|
err := app.Run()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
v3/internal/parser/testdata/struct_literal_multiple_other/services/other.go
vendored
Normal file
12
v3/internal/parser/testdata/struct_literal_multiple_other/services/other.go
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
// OtherService is a struct
|
||||||
|
// that does things
|
||||||
|
type OtherService struct {
|
||||||
|
t int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yay does this and that
|
||||||
|
func (o *OtherService) Yay() {
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user