mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 23:51:44 +08:00
[v3] MUCH Improved parser for bound structs
This commit is contained in:
parent
93aa8345dc
commit
95bb15eef4
@ -4,6 +4,7 @@ go 1.19
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-task/task/v3 v3.20.0
|
github.com/go-task/task/v3 v3.20.0
|
||||||
|
github.com/google/go-cmp v0.5.9
|
||||||
github.com/jackmordaunt/icns/v2 v2.2.1
|
github.com/jackmordaunt/icns/v2 v2.2.1
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/leaanthony/clir v1.6.0
|
github.com/leaanthony/clir v1.6.0
|
||||||
@ -12,17 +13,14 @@ require (
|
|||||||
github.com/matryer/is v1.4.0
|
github.com/matryer/is v1.4.0
|
||||||
github.com/pterm/pterm v0.12.51
|
github.com/pterm/pterm v0.12.51
|
||||||
github.com/samber/lo v1.37.0
|
github.com/samber/lo v1.37.0
|
||||||
github.com/stretchr/testify v1.8.1
|
|
||||||
github.com/tc-hib/winres v0.1.6
|
github.com/tc-hib/winres v0.1.6
|
||||||
github.com/wailsapp/wails/v2 v2.3.2-0.20230117193915-45c3a501d9e6
|
github.com/wailsapp/wails/v2 v2.3.2-0.20230117193915-45c3a501d9e6
|
||||||
golang.org/x/tools v0.1.12
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
atomicgo.dev/cursor v0.1.1 // indirect
|
atomicgo.dev/cursor v0.1.1 // indirect
|
||||||
atomicgo.dev/keyboard v0.2.8 // indirect
|
atomicgo.dev/keyboard v0.2.8 // indirect
|
||||||
github.com/containerd/console v1.0.3 // indirect
|
github.com/containerd/console v1.0.3 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/fatih/color v1.13.0 // indirect
|
github.com/fatih/color v1.13.0 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
github.com/gookit/color v1.5.2 // indirect
|
github.com/gookit/color v1.5.2 // indirect
|
||||||
@ -39,7 +37,6 @@ require (
|
|||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
||||||
github.com/radovskyb/watcher v1.0.7 // indirect
|
github.com/radovskyb/watcher v1.0.7 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/sajari/fuzzy v1.0.0 // indirect
|
github.com/sajari/fuzzy v1.0.0 // indirect
|
||||||
@ -47,7 +44,6 @@ require (
|
|||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||||
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 // indirect
|
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 // indirect
|
||||||
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 // indirect
|
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
golang.org/x/sys v0.3.0 // indirect
|
golang.org/x/sys v0.3.0 // indirect
|
||||||
|
@ -26,6 +26,7 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78
|
|||||||
github.com/go-task/task/v3 v3.20.0 h1:pTavuhP+AiEpKLzh5I6Lja9Ux7ypYO5QMsEPTbhYEDc=
|
github.com/go-task/task/v3 v3.20.0 h1:pTavuhP+AiEpKLzh5I6Lja9Ux7ypYO5QMsEPTbhYEDc=
|
||||||
github.com/go-task/task/v3 v3.20.0/go.mod h1:y7rWakbLR5gFElGgo6rA2dyr6vU/zNIDVfn3S4Of6OI=
|
github.com/go-task/task/v3 v3.20.0/go.mod h1:y7rWakbLR5gFElGgo6rA2dyr6vU/zNIDVfn3S4Of6OI=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||||
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
||||||
@ -113,7 +114,6 @@ github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z
|
|||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
@ -122,7 +122,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/tc-hib/winres v0.1.6 h1:qgsYHze+BxQPEYilxIz/KCQGaClvI2+yLBAZs+3+0B8=
|
github.com/tc-hib/winres v0.1.6 h1:qgsYHze+BxQPEYilxIz/KCQGaClvI2+yLBAZs+3+0B8=
|
||||||
github.com/tc-hib/winres v0.1.6/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A=
|
github.com/tc-hib/winres v0.1.6/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A=
|
||||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||||
@ -136,8 +135,6 @@ golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p
|
|||||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI=
|
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI=
|
||||||
golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
|
||||||
@ -172,8 +169,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
|
@ -3,89 +3,76 @@ package parser
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/samber/lo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var Debug = false
|
var packageCache = make(map[string]*ParsedPackage)
|
||||||
|
|
||||||
func debug(msg string, args ...interface{}) {
|
type packageName = string
|
||||||
if Debug {
|
type structName = string
|
||||||
println(fmt.Sprintf(msg, args...))
|
|
||||||
}
|
type Parameter struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
IsStruct bool
|
||||||
|
IsSlice bool
|
||||||
|
IsPointer bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type BoundStruct struct {
|
type BoundMethod struct {
|
||||||
|
Name string
|
||||||
|
DocComment string
|
||||||
|
Inputs []*Parameter
|
||||||
|
Outputs []*Parameter
|
||||||
|
}
|
||||||
|
|
||||||
|
type Model struct {
|
||||||
|
Name string
|
||||||
|
Fields []*Field
|
||||||
|
}
|
||||||
|
|
||||||
|
type Field struct {
|
||||||
Name string
|
Name string
|
||||||
Methods map[string]*FuncSignature
|
Type string
|
||||||
Comments []string
|
IsStruct bool
|
||||||
|
IsSlice bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type parsedPackage struct {
|
type ParsedPackage struct {
|
||||||
name string
|
Pkg *ast.Package
|
||||||
pkg *ast.Package
|
|
||||||
boundStructs map[string]*BoundStruct
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Context struct {
|
type Project struct {
|
||||||
packages map[string]*parsedPackage
|
Path string
|
||||||
dir string
|
BoundMethods map[packageName]map[structName][]*BoundMethod
|
||||||
|
Models map[packageName]map[structName]*Model
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) findImportPackage(pkgName string, pkg *ast.Package) (*ast.Package, error) {
|
func ParseProject(projectPath string) (*Project, error) {
|
||||||
for _, file := range pkg.Files {
|
result := &Project{
|
||||||
for _, imp := range file.Imports {
|
BoundMethods: make(map[packageName]map[structName][]*BoundMethod),
|
||||||
path, err := strconv.Unquote(imp.Path.Value)
|
Models: make(map[packageName]map[structName]*Model),
|
||||||
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)
|
pkgs, err := result.parseDirectory(projectPath)
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) getPackageFromPath(path string) (*ast.Package, error) {
|
|
||||||
dir, err := filepath.Abs(c.dir)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !filepath.IsAbs(path) {
|
println("Parsed " + projectPath)
|
||||||
dir = filepath.Join(dir, path)
|
err = result.findApplicationNewCalls(pkgs)
|
||||||
} 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for impName, impPkg := range impPkg {
|
return result, nil
|
||||||
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 (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error) {
|
||||||
|
println("Parsing directory " + dir)
|
||||||
|
if packageCache[dir] != nil {
|
||||||
|
println("Found directory in cache!")
|
||||||
|
return map[string]*ParsedPackage{dir: packageCache[dir]}, nil
|
||||||
|
}
|
||||||
// Parse the directory
|
// Parse the directory
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
if dir == "." || dir == "" {
|
if dir == "." || dir == "" {
|
||||||
@ -95,70 +82,28 @@ func ParseDirectory(dir string) (*Context, error) {
|
|||||||
}
|
}
|
||||||
dir = cwd
|
dir = cwd
|
||||||
}
|
}
|
||||||
println("Parsing directory " + dir)
|
|
||||||
pkgs, err := parser.ParseDir(fset, dir, nil, parser.ParseComments)
|
pkgs, err := parser.ParseDir(fset, dir, nil, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var result = make(map[string]*ParsedPackage)
|
||||||
context := &Context{
|
for packageName, pkg := range pkgs {
|
||||||
dir: dir,
|
parsedPackage := &ParsedPackage{Pkg: pkg}
|
||||||
packages: make(map[string]*parsedPackage),
|
packageCache[dir] = parsedPackage
|
||||||
|
result[packageName] = parsedPackage
|
||||||
}
|
}
|
||||||
|
return result, nil
|
||||||
// Iterate through the packages
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
context.packages[pkg.Name] = &parsedPackage{
|
|
||||||
name: pkg.Name,
|
|
||||||
pkg: pkg,
|
|
||||||
boundStructs: make(map[string]*BoundStruct),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
findApplicationNewCalls(context)
|
|
||||||
err = findStructDefinitions(context)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return context, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func findStructDefinitions(context *Context) error {
|
func (p *Project) findApplicationNewCalls(pkgs map[string]*ParsedPackage) (err 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) {
|
var callFound bool
|
||||||
println("Finding application.New calls")
|
|
||||||
// Iterate through the packages
|
|
||||||
currentPackages := lo.Keys(context.packages)
|
|
||||||
|
|
||||||
for _, packageName := range currentPackages {
|
for packageName, pkg := range pkgs {
|
||||||
thisPackage := context.packages[packageName]
|
thisPackage := pkg.Pkg
|
||||||
debug("Parsing package: %s", packageName)
|
println(" - Looking in package: " + packageName)
|
||||||
// Iterate through the package's files
|
// Iterate through the package's files
|
||||||
for _, file := range thisPackage.pkg.Files {
|
for _, file := range thisPackage.Files {
|
||||||
// Use an ast.Inspector to find the calls to application.New
|
// Use an ast.Inspector to find the calls to application.New
|
||||||
ast.Inspect(file, func(n ast.Node) bool {
|
ast.Inspect(file, func(n ast.Node) bool {
|
||||||
// Check if the node is a call expression
|
// Check if the node is a call expression
|
||||||
@ -225,6 +170,7 @@ func findApplicationNewCalls(context *Context) {
|
|||||||
if _, ok := arrayType.Elt.(*ast.InterfaceType); !ok {
|
if _, ok := arrayType.Elt.(*ast.InterfaceType); !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
callFound = true
|
||||||
// Iterate through the slice elements
|
// Iterate through the slice elements
|
||||||
for _, elt := range sliceExpr.Elts {
|
for _, elt := range sliceExpr.Elts {
|
||||||
// Check the element is a unary expression
|
// Check the element is a unary expression
|
||||||
@ -243,46 +189,59 @@ func findApplicationNewCalls(context *Context) {
|
|||||||
// Check if the lit is an ident
|
// Check if the lit is an ident
|
||||||
ident, ok := boundStructLit.Type.(*ast.Ident)
|
ident, ok := boundStructLit.Type.(*ast.Ident)
|
||||||
if ok {
|
if ok {
|
||||||
if ident.Obj == nil {
|
err = p.parseBoundStructMethods(ident.Name, thisPackage)
|
||||||
thisPackage.boundStructs[ident.Name] = &BoundStruct{
|
if err != nil {
|
||||||
Name: ident.Name,
|
return true
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
// Check if the ident is a struct type
|
// Check if the ident is a struct type
|
||||||
if _, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
//if typeSpec, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
||||||
thisPackage.boundStructs[ident.Name] = &BoundStruct{
|
// var parsedStruct *StructDefinition
|
||||||
Name: ident.Name,
|
// parsedStruct, err = p.parseStruct(typeSpec, thisPackage)
|
||||||
}
|
// if err != nil {
|
||||||
continue
|
// return true
|
||||||
}
|
// }
|
||||||
|
// p.addModel(thisPackage.Name, parsedStruct)
|
||||||
|
// p.addBoundStruct(thisPackage.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
|
// Check the typespec decl is a struct
|
||||||
if _, ok := ident.Obj.Decl.(*ast.StructType); ok {
|
//if _, ok := ident.Obj.Decl.(*ast.StructType); ok {
|
||||||
continue
|
// continue
|
||||||
}
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
// 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 {
|
||||||
// Check if the selector is an ident
|
// Check if the selector is an ident
|
||||||
if ident, ok := selector.X.(*ast.Ident); ok {
|
if _, ok := selector.X.(*ast.Ident); ok {
|
||||||
// Check if the ident is a package
|
//// Check if the ident is a package
|
||||||
if _, ok := context.packages[ident.Name]; !ok {
|
//if _, ok := context.packages[ident.Name]; !ok {
|
||||||
externalPackage, err := context.getPackageFromPath(ident.Name)
|
// externalPackage, err := context.getPackageFromPath(ident.Name)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
println("Error getting package from path: " + err.Error())
|
// println("Error getting package from path: " + err.Error())
|
||||||
return true
|
// return true
|
||||||
}
|
// }
|
||||||
context.packages[ident.Name] = &parsedPackage{
|
// context.packages[ident.Name] = &parsedPackage{
|
||||||
name: ident.Name,
|
// name: ident.Name,
|
||||||
pkg: externalPackage,
|
// pkg: externalPackage,
|
||||||
boundStructs: make(map[string]*BoundStruct),
|
// boundStructs: make(map[string]*BoundStruct),
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
context.packages[ident.Name].boundStructs[selector.Sel.Name] = &BoundStruct{
|
//context.packages[ident.Name].boundStructs[selector.Sel.Name] = &BoundStruct{
|
||||||
Name: selector.Sel.Name,
|
// Name: selector.Sel.Name,
|
||||||
}
|
//}
|
||||||
|
//p.parseStructFromExternalPackage(selector.Sel.Name, ident.Name, thisPackage)
|
||||||
|
//p.addBoundStruct(ident.Name, selector.Sel.Name)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -293,166 +252,121 @@ func findApplicationNewCalls(context *Context) {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
if !callFound {
|
||||||
}
|
return fmt.Errorf("no Bound structs found")
|
||||||
|
|
||||||
//type Method struct {
|
|
||||||
// Name string
|
|
||||||
// Type *ast.FuncType
|
|
||||||
//}
|
|
||||||
|
|
||||||
//func getStructTypeSpec(pkg *ast.Package, structName string) (*ast.TypeSpec, []Method) {
|
|
||||||
// var typeSpec *ast.TypeSpec
|
|
||||||
// var methods []Method
|
|
||||||
//
|
|
||||||
// // 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 {
|
|
||||||
// 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 {
|
|
||||||
// // Add the method to the list of methods
|
|
||||||
// method := Method{
|
|
||||||
// Name: funcDecl.Name.Name,
|
|
||||||
// Type: funcDecl.Type,
|
|
||||||
// }
|
|
||||||
// methods = append(methods, method)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return typeSpec, methods
|
|
||||||
//}
|
|
||||||
|
|
||||||
type Arg struct {
|
|
||||||
Name string
|
|
||||||
Type string
|
|
||||||
}
|
|
||||||
|
|
||||||
type FuncSignature struct {
|
|
||||||
Comments []string
|
|
||||||
Inputs []Arg
|
|
||||||
Outputs []Arg
|
|
||||||
}
|
|
||||||
|
|
||||||
func FuncTypeToSignature(ft *ast.FuncType) *FuncSignature {
|
|
||||||
sig := &FuncSignature{}
|
|
||||||
|
|
||||||
// process input arguments
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// process output arguments
|
func (p *Project) addBoundMethods(packageName string, name string, boundMethods []*BoundMethod) {
|
||||||
if ft.Results != nil {
|
_, ok := p.BoundMethods[packageName]
|
||||||
for _, field := range ft.Results.List {
|
if !ok {
|
||||||
arg := Arg{}
|
p.BoundMethods[packageName] = make(map[structName][]*BoundMethod)
|
||||||
arg.Type = tokenToString(field.Type)
|
|
||||||
sig.Outputs = append(sig.Outputs, arg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
p.BoundMethods[packageName][name] = boundMethods
|
||||||
return sig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func tokenToString(t ast.Expr) string {
|
func (p *Project) parseBoundStructMethods(name string, pkg *ast.Package) error {
|
||||||
switch t := t.(type) {
|
var methods []*BoundMethod
|
||||||
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 ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// Iterate over all files in the package
|
||||||
for _, file := range pkg.Files {
|
for _, file := range pkg.Files {
|
||||||
// Iterate over all declarations in the file
|
// Iterate over all declarations in the file
|
||||||
for _, decl := range file.Decls {
|
for _, decl := range file.Decls {
|
||||||
// Check if the declaration is a type declaration
|
// Check if the declaration is a type declaration
|
||||||
if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.TYPE {
|
if funcDecl, ok := decl.(*ast.FuncDecl); ok && funcDecl.Recv != nil {
|
||||||
// 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
|
// Check if the function has a receiver argument of the struct type
|
||||||
recvType, ok := funcDecl.Recv.List[0].Type.(*ast.StarExpr)
|
recvType, ok := funcDecl.Recv.List[0].Type.(*ast.StarExpr)
|
||||||
if ok {
|
if ok {
|
||||||
if ident, ok := recvType.X.(*ast.Ident); ok && ident.Name == structName {
|
if ident, ok := recvType.X.(*ast.Ident); ok && ident.Name == name {
|
||||||
// Get comments associated with the method
|
// Add the method to the list of methods
|
||||||
if funcDecl.Doc != nil {
|
method := &BoundMethod{
|
||||||
var comments []string
|
Name: funcDecl.Name.Name,
|
||||||
for _, comment := range funcDecl.Doc.List {
|
DocComment: funcDecl.Doc.Text(),
|
||||||
comments = append(comments, comment.Text)
|
Inputs: make([]*Parameter, 0),
|
||||||
}
|
Outputs: make([]*Parameter, 0),
|
||||||
// Add the method to the list of methods
|
|
||||||
method := Method{
|
|
||||||
Name: funcDecl.Name.Name,
|
|
||||||
Type: funcDecl.Type,
|
|
||||||
Comments: comments,
|
|
||||||
}
|
|
||||||
methods = append(methods, method)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
method.Inputs = p.parseParameters(funcDecl.Type.Params)
|
||||||
|
method.Outputs = p.parseParameters(funcDecl.Type.Results)
|
||||||
|
|
||||||
|
methods = append(methods, method)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
p.addBoundMethods(pkg.Name, name, methods)
|
||||||
return typeSpec, methods, structComments
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Project) parseParameters(params *ast.FieldList) []*Parameter {
|
||||||
|
var result []*Parameter
|
||||||
|
for _, field := range params.List {
|
||||||
|
var theseFields []*Parameter
|
||||||
|
if len(field.Names) > 0 {
|
||||||
|
for _, name := range field.Names {
|
||||||
|
theseFields = append(theseFields, &Parameter{
|
||||||
|
Name: name.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
theseFields = append(theseFields, &Parameter{
|
||||||
|
Name: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// loop over fields
|
||||||
|
for _, thisField := range theseFields {
|
||||||
|
thisField.Type = getTypeString(field.Type)
|
||||||
|
switch t := field.Type.(type) {
|
||||||
|
case *ast.StarExpr:
|
||||||
|
thisField.IsStruct = isStructType(t.X)
|
||||||
|
thisField.IsPointer = true
|
||||||
|
case *ast.StructType:
|
||||||
|
thisField.IsStruct = true
|
||||||
|
case *ast.ArrayType:
|
||||||
|
thisField.IsSlice = true
|
||||||
|
thisField.IsStruct = isStructType(t.Elt)
|
||||||
|
case *ast.MapType:
|
||||||
|
thisField.IsSlice = true
|
||||||
|
thisField.IsStruct = isStructType(t.Value)
|
||||||
|
}
|
||||||
|
result = append(result, thisField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTypeString(expr ast.Expr) string {
|
||||||
|
switch t := expr.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
return t.Name
|
||||||
|
case *ast.StarExpr:
|
||||||
|
return getTypeString(t.X)
|
||||||
|
case *ast.ArrayType:
|
||||||
|
return getTypeString(t.Elt)
|
||||||
|
//case *ast.MapType:
|
||||||
|
// return "map[" + getTypeString(t.Key) + "]" + getTypeString(t.Value)
|
||||||
|
default:
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isStructType(expr ast.Expr) bool {
|
||||||
|
switch e := expr.(type) {
|
||||||
|
case *ast.StructType:
|
||||||
|
return true
|
||||||
|
case *ast.StarExpr:
|
||||||
|
return isStructType(e.X)
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
return isStructType(e.Sel)
|
||||||
|
case *ast.ArrayType:
|
||||||
|
return isStructType(e.Elt)
|
||||||
|
case *ast.SliceExpr:
|
||||||
|
return isStructType(e.X)
|
||||||
|
case *ast.Ident:
|
||||||
|
return e.Obj != nil && e.Obj.Kind == ast.Typ
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,60 +3,215 @@ package parser
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/samber/lo"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseDirectory(t *testing.T) {
|
func TestParseDirectory(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
dir string
|
dir string
|
||||||
want []string
|
wantBoundMethods map[string]map[string][]*BoundMethod
|
||||||
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: []string{"main.GreetService"},
|
//wantModels: []string{"main.GreetService"},
|
||||||
wantErr: false,
|
wantBoundMethods: map[string]map[string][]*BoundMethod{
|
||||||
},
|
"main": {
|
||||||
{
|
"GreetService": {
|
||||||
name: "should find multiple bound services",
|
{
|
||||||
dir: "testdata/struct_literal_multiple",
|
Name: "Greet",
|
||||||
want: []string{"main.GreetService", "main.OtherService"},
|
DocComment: "Greet someone\n",
|
||||||
wantErr: false,
|
Inputs: []*Parameter{
|
||||||
},
|
{
|
||||||
{
|
Name: "name",
|
||||||
name: "should find multiple bound services over multiple files",
|
Type: "string",
|
||||||
dir: "testdata/struct_literal_multiple_files",
|
IsStruct: false,
|
||||||
want: []string{"main.GreetService", "main.OtherService"},
|
IsSlice: false,
|
||||||
wantErr: false,
|
},
|
||||||
},
|
},
|
||||||
{
|
Outputs: []*Parameter{
|
||||||
name: "should find multiple bound services over multiple packages",
|
{
|
||||||
dir: "testdata/struct_literal_multiple_other",
|
Name: "",
|
||||||
want: []string{"main.GreetService", "services.OtherService"},
|
Type: "string",
|
||||||
|
IsStruct: false,
|
||||||
|
IsSlice: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "NoInputsStringOut",
|
||||||
|
DocComment: "",
|
||||||
|
Inputs: nil,
|
||||||
|
Outputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Name: "",
|
||||||
|
Type: "string",
|
||||||
|
IsStruct: false,
|
||||||
|
IsSlice: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "StringArrayInputStringOut",
|
||||||
|
Inputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Name: "in",
|
||||||
|
Type: "string",
|
||||||
|
IsSlice: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "StringArrayInputStringArrayOut",
|
||||||
|
Inputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Name: "in",
|
||||||
|
Type: "string",
|
||||||
|
IsSlice: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Type: "string",
|
||||||
|
IsSlice: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "StringArrayInputNamedOutput",
|
||||||
|
Inputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Name: "in",
|
||||||
|
Type: "string",
|
||||||
|
IsSlice: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Name: "output",
|
||||||
|
Type: "string",
|
||||||
|
IsSlice: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "StringArrayInputNamedOutputs",
|
||||||
|
Inputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Name: "in",
|
||||||
|
Type: "string",
|
||||||
|
IsSlice: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Name: "output",
|
||||||
|
Type: "string",
|
||||||
|
IsSlice: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "err",
|
||||||
|
Type: "error",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "IntPointerInputNamedOutputs",
|
||||||
|
Inputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Name: "in",
|
||||||
|
Type: "int",
|
||||||
|
IsPointer: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Name: "output",
|
||||||
|
Type: "int",
|
||||||
|
IsPointer: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "err",
|
||||||
|
Type: "error",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "StructPointerInputErrorOutput",
|
||||||
|
Inputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Name: "in",
|
||||||
|
Type: "Person",
|
||||||
|
IsStruct: true,
|
||||||
|
IsPointer: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Type: "error",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "StructPointerInputStructPointerOutput",
|
||||||
|
Inputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Name: "in",
|
||||||
|
Type: "Person",
|
||||||
|
IsStruct: true,
|
||||||
|
IsPointer: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outputs: []*Parameter{
|
||||||
|
{
|
||||||
|
Type: "Person",
|
||||||
|
IsPointer: true,
|
||||||
|
IsStruct: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
//{
|
||||||
|
// name: "should find multiple bound services",
|
||||||
|
// dir: "testdata/struct_literal_multiple",
|
||||||
|
// //wantModels: []string{"main.GreetService", "main.OtherService"},
|
||||||
|
// wantErr: false,
|
||||||
|
//},
|
||||||
|
//{
|
||||||
|
// name: "should find multiple bound services over multiple files",
|
||||||
|
// dir: "testdata/struct_literal_multiple_files",
|
||||||
|
// //wantModels: []string{"main.GreetService", "main.OtherService"},
|
||||||
|
// wantErr: false,
|
||||||
|
//},
|
||||||
|
//{
|
||||||
|
// name: "should find multiple bound services over multiple packages",
|
||||||
|
// dir: "testdata/struct_literal_multiple_other",
|
||||||
|
// //wantModels: []string{"main.GreetService", "services.OtherService", "main.Person"},
|
||||||
|
// 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
|
got, err := ParseProject(tt.dir)
|
||||||
got, 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
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, pkg := range got.packages {
|
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||||
for structName, structType := range pkg.boundStructs {
|
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||||
require.NotNil(t, structType)
|
|
||||||
require.True(t, lo.Contains(tt.want, name+"."+structName))
|
|
||||||
tt.want = lo.Without(tt.want, name+"."+structName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
require.Empty(t, tt.want)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,13 +221,13 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
// tests := []struct {
|
// tests := []struct {
|
||||||
// name string
|
// name string
|
||||||
// dir string
|
// dir string
|
||||||
// want string
|
// wantModels 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 {
|
// wantModels: `namespace main {
|
||||||
// class GreetService {
|
// class GreetService {
|
||||||
// SomeVariable: number;
|
// SomeVariable: number;
|
||||||
// }
|
// }
|
||||||
@ -83,7 +238,7 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
// {
|
// {
|
||||||
// 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 {
|
// wantModels: `namespace main {
|
||||||
// class GreetService {
|
// class GreetService {
|
||||||
// SomeVariable: number;
|
// SomeVariable: number;
|
||||||
// }
|
// }
|
||||||
@ -96,7 +251,7 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
// {
|
// {
|
||||||
// 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 {
|
// wantModels: `namespace main {
|
||||||
// class GreetService {
|
// class GreetService {
|
||||||
// SomeVariable: number;
|
// SomeVariable: number;
|
||||||
// }
|
// }
|
||||||
@ -109,7 +264,7 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
// {
|
// {
|
||||||
// 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 {
|
// wantModels: `namespace main {
|
||||||
// class localStruct {
|
// class localStruct {
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
@ -139,7 +294,7 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
//
|
//
|
||||||
// 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.wantModels, string(ts))
|
||||||
//
|
//
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
|
@ -12,6 +12,11 @@ import (
|
|||||||
type GreetService struct {
|
type GreetService struct {
|
||||||
SomeVariable int
|
SomeVariable int
|
||||||
lowerCase string
|
lowerCase string
|
||||||
|
target *Person
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Greet does XYZ
|
// Greet does XYZ
|
||||||
@ -19,6 +24,11 @@ func (*GreetService) Greet(name string) string {
|
|||||||
return "Hello " + name
|
return "Hello " + name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPerson creates a new person
|
||||||
|
func (*GreetService) NewPerson(name string) *Person {
|
||||||
|
return &Person{Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := application.New(application.Options{
|
app := application.New(application.Options{
|
||||||
Bind: []interface{}{
|
Bind: []interface{}{
|
||||||
|
@ -7,6 +7,6 @@ type OtherService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Yay does this and that
|
// Yay does this and that
|
||||||
func (o *OtherService) Yay() {
|
func (o *OtherService) Yay() []int {
|
||||||
|
return []int{0, 1, 2}
|
||||||
}
|
}
|
||||||
|
@ -3,19 +3,58 @@ package main
|
|||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreetService is great
|
||||||
type GreetService struct {
|
type GreetService struct {
|
||||||
SomeVariable int
|
SomeVariable int
|
||||||
lowerCase string
|
lowerCase string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Greet someone
|
||||||
func (*GreetService) Greet(name string) string {
|
func (*GreetService) Greet(name string) string {
|
||||||
return "Hello " + name
|
return "Hello " + name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*GreetService) NoInputsStringOut() string {
|
||||||
|
return "Hello"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GreetService) StringArrayInputStringOut(in []string) string {
|
||||||
|
return strings.Join(in, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GreetService) StringArrayInputStringArrayOut(in []string) []string {
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GreetService) StringArrayInputNamedOutput(in []string) (output []string) {
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GreetService) StringArrayInputNamedOutputs(in []string) (output []string, err error) {
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GreetService) IntPointerInputNamedOutputs(in *int) (output *int, err error) {
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GreetService) StructPointerInputErrorOutput(in *Person) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GreetService) StructPointerInputStructPointerOutput(in *Person) *Person {
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := application.New(application.Options{
|
app := application.New(application.Options{
|
||||||
Bind: []interface{}{
|
Bind: []interface{}{
|
||||||
|
Loading…
Reference in New Issue
Block a user