diff --git a/exp/examples/binding/main.go b/exp/examples/binding/main.go index 4fb639008..dc77f289b 100644 --- a/exp/examples/binding/main.go +++ b/exp/examples/binding/main.go @@ -4,23 +4,19 @@ import ( _ "embed" "log" + "github.com/wailsapp/wails/exp/examples/binding/services" + "github.com/wailsapp/wails/exp/pkg/application" "github.com/wailsapp/wails/exp/pkg/options" ) -type GreetService struct { - SomeVariable int - lowerCase string -} - -func (*GreetService) Greet(name string) string { - return "Hello " + name -} +type localStruct struct{} func main() { app := application.New(options.Application{ Bind: []interface{}{ - &GreetService{}, + &localStruct{}, + &services.GreetService{}, }, }) diff --git a/exp/examples/binding/service.go b/exp/examples/binding/service.go deleted file mode 100644 index ee8ef3cd8..000000000 --- a/exp/examples/binding/service.go +++ /dev/null @@ -1,51 +0,0 @@ -package main - -import ( - "github.com/wailsapp/wails/exp/pkg/application" - "github.com/wailsapp/wails/exp/pkg/options" -) - -//func appOptions() options.Application { -// return options.Application{ -// Bind: []interface{}{ -// &GreetService{}, -// }, -// } -//} - -//func appOptions() options.Application { -// result := options.Application{ -// Bind: []interface{}{ -// &GreetService{}, -// }, -// } -// return result -//} - -func appOptions() options.Application { - result := options.Application{ - Bind: []interface{}{ - &GreetService{}, - }, - } - return result -} - -func run() error { - //o := options.Application{ - // Bind: []interface{}{ - // &GreetService{}, - // }, - //} - //app := application.New(o) - - //app := application.New(options.Application{ - // Bind: []interface{}{ - // &GreetService{}, - // }, - //}) - - app := application.New(appOptions()) - - return app.Run() -} diff --git a/exp/examples/binding/services/GreetService.go b/exp/examples/binding/services/GreetService.go new file mode 100644 index 000000000..61afaffdd --- /dev/null +++ b/exp/examples/binding/services/GreetService.go @@ -0,0 +1,13 @@ +package services + +import "github.com/wailsapp/wails/exp/examples/binding/models" + +type GreetService struct { + SomeVariable int + lowercase string + Parent *models.Person +} + +func (*GreetService) Greet(name string) string { + return "Hello " + name +} diff --git a/exp/go.mod b/exp/go.mod index c4a0aca45..62732c088 100644 --- a/exp/go.mod +++ b/exp/go.mod @@ -11,6 +11,7 @@ require ( github.com/samber/lo v1.37.0 github.com/stretchr/testify v1.8.1 github.com/tc-hib/winres v0.1.6 + golang.org/x/tools v0.1.12 ) require ( @@ -37,6 +38,7 @@ require ( github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 // 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/sync v0.1.0 // indirect golang.org/x/sys v0.3.0 // indirect golang.org/x/term v0.3.0 // indirect diff --git a/exp/go.sum b/exp/go.sum index 528092f09..2b249a9e5 100644 --- a/exp/go.sum +++ b/exp/go.sum @@ -116,6 +116,8 @@ 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-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI= 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/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -145,6 +147,8 @@ 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/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.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 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= diff --git a/exp/internal/parser/boundstructs.go b/exp/internal/parser/boundstructs.go deleted file mode 100644 index 5141aeb7a..000000000 --- a/exp/internal/parser/boundstructs.go +++ /dev/null @@ -1,47 +0,0 @@ -package parser - -import "go/ast" - -type structInfo struct { - packageName string - structName string -} - -func extractBindExprs(imp *ImportSpecInfo) { - // Check if the given expression is a composite literal - if comp, ok := (*imp.Options).(*ast.CompositeLit); ok { - // Iterate through the composite literal fields - for _, field := range comp.Elts { - // Check if the field key is "Bind" - if kv, ok := field.(*ast.KeyValueExpr); ok { - if ident, ok := kv.Key.(*ast.Ident); ok { - if ident.Name == "Bind" { - // Extract the expressions in the Bind field - if arr, ok := kv.Value.(*ast.CompositeLit); ok { - for _, elt := range arr.Elts { - if unaryExpr, ok := elt.(*ast.UnaryExpr); ok { - if addr, ok := unaryExpr.X.(*ast.CompositeLit); ok { - // Extract the type of the struct - if selExpr, ok := addr.Type.(*ast.SelectorExpr); ok { - if ident, ok := selExpr.X.(*ast.Ident); ok { - imp.BoundStructNames = append(imp.BoundStructNames, &structInfo{ - packageName: ident.Name, - structName: selExpr.Sel.Name, - }) - } - } else if ident, ok := addr.Type.(*ast.Ident); ok { - imp.BoundStructNames = append(imp.BoundStructNames, &structInfo{ - packageName: "", - structName: ident.Name, - }) - } - } - } - } - } - } - } - } - } - } -} diff --git a/exp/internal/parser/boundstructs_test.go b/exp/internal/parser/boundstructs_test.go deleted file mode 100644 index c91e3c65f..000000000 --- a/exp/internal/parser/boundstructs_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_extractBindExprs(t *testing.T) { - tests := []struct { - name string - dir string - expectedBoundStructs []*structInfo - }{ - { - name: "should find single bound struct literal", - dir: "testdata/boundstructs/struct_literal_single", - expectedBoundStructs: []*structInfo{{structName: "GreetService"}}, - }, - { - name: "should find multiple bound struct literal", - dir: "testdata/boundstructs/struct_literal_multiple", - expectedBoundStructs: []*structInfo{{structName: "GreetService"}, {structName: "OtherService"}}, - }, - { - name: "should find multiple bound struct literals over multiple files", - dir: "testdata/boundstructs/struct_literal_multiple_files", - expectedBoundStructs: []*structInfo{{structName: "GreetService"}, {structName: "OtherService"}}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - imports, err := findFilesImportingPackage(tt.dir, "github.com/wailsapp/wails/exp/pkg/application") - require.NoError(t, err) - imp := findNewCalls(imports) - require.NotNil(t, imp) - isOptions := isOptionsApplication(imp.Options) - require.True(t, isOptions) - extractBindExprs(imp) - require.ElementsMatchf(t, tt.expectedBoundStructs, imp.BoundStructNames, "expected %v, got %v", tt.expectedBoundStructs, imp.BoundStructNames) - - }) - } -} diff --git a/exp/internal/parser/calls.go b/exp/internal/parser/calls.go deleted file mode 100644 index 69bfca501..000000000 --- a/exp/internal/parser/calls.go +++ /dev/null @@ -1,66 +0,0 @@ -package parser - -import ( - "go/ast" - "os" -) - -func findNewCalls(imports []ImportSpecInfo) *ImportSpecInfo { - - var result *ast.Expr - var inImport *ImportSpecInfo - - for _, imp := range imports { - for _, decl := range imp.File.Decls { - ast.Inspect(decl, func(n ast.Node) bool { - // check if the current node is a function call - callExpr, ok := n.(*ast.CallExpr) - if !ok { - return true - } - - // check if the function being called is .New() - selExpr, ok := callExpr.Fun.(*ast.SelectorExpr) - if !ok || selExpr.Sel.Name != "New" { - return true - } - - // Get the name of the thing that New is being called on. - var receiverName string - switch rcv := selExpr.X.(type) { - case *ast.Ident: - receiverName = rcv.Name - case *ast.SelectorExpr: - receiverName = rcv.Sel.Name - default: - receiverName = "unknown" - } - - // Check if the receiver is a package name - for _, i := range imports { - if i.Identifier() == receiverName { - if i.ImportSpec.Path.Value == `"github.com/wailsapp/wails/exp/pkg/application"` { - // We have a call to application.New() - // Parse out the first argument and check it is an option.Application struct - // check callExpr.Args[0] is a struct literal - if len(callExpr.Args) != 1 { - return true - } - if result != nil { - println("Found more than one call to application.New()") - os.Exit(1) - } - result = &(callExpr.Args[0]) - inImport = &imp - - return false - } - } - } - return true - }) - } - } - inImport.Options = result - return inImport -} diff --git a/exp/internal/parser/calls_test.go b/exp/internal/parser/calls_test.go deleted file mode 100644 index 0aeaf2c38..000000000 --- a/exp/internal/parser/calls_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_findNewCalls(t *testing.T) { - tests := []struct { - name string - dir string - expectedExpressions int - }{ - { - name: "should find single call to application.New", - dir: "testdata/boundstructs/struct_literal_single", - expectedExpressions: 1, - }, - { - name: "should find single call to application.New in multiple files", - dir: "testdata/boundstructs/struct_literal_multiple_files", - expectedExpressions: 1, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - imports, err := findFilesImportingPackage(tt.dir, "github.com/wailsapp/wails/exp/pkg/application") - require.NoError(t, err) - got := findNewCalls(imports) - require.NotNil(t, got.Options) - }) - } -} diff --git a/exp/internal/parser/imports.go b/exp/internal/parser/imports.go deleted file mode 100644 index f43814ad5..000000000 --- a/exp/internal/parser/imports.go +++ /dev/null @@ -1,69 +0,0 @@ -package parser - -import ( - "go/ast" - "go/parser" - "go/token" - "os" - "path/filepath" - "strings" -) - -type ImportSpecInfo struct { - FileName string - ImportSpec *ast.ImportSpec - File *ast.File - Alias string - Options *ast.Expr - BoundStructNames []*structInfo -} - -func (i ImportSpecInfo) Identifier() string { - if i.Alias != "" { - return i.Alias - } - return strings.Trim(filepath.Base(i.ImportSpec.Path.Value), `"`) -} - -func findFilesImportingPackage(dir, pkg string) ([]ImportSpecInfo, error) { - var importSpecInfo []ImportSpecInfo - - // Recursively search for .go files in the given directory - err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if filepath.Ext(path) != ".go" { - return nil - } - - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, path, nil, parser.AllErrors) - if err != nil { - return err - } - - // Search for import statement in the AST - for _, imp := range f.Imports { - if imp.Path.Value == "\""+pkg+"\"" { - var alias string - if imp.Name != nil { - alias = imp.Name.Name - } - importSpecInfo = append(importSpecInfo, ImportSpecInfo{ - FileName: path, - ImportSpec: imp, - Alias: alias, - File: f, - }) - } - } - return nil - }) - - if err != nil { - return nil, err - } - - return importSpecInfo, nil -} diff --git a/exp/internal/parser/imports_test.go b/exp/internal/parser/imports_test.go deleted file mode 100644 index 70dbd86d4..000000000 --- a/exp/internal/parser/imports_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/samber/lo" -) - -func Test_findFilesImportingPackage(t *testing.T) { - tests := []struct { - name string - dir string - pkg string - want []string - wantErr bool - }{ - { - name: "should identify single file importing package", - dir: "testdata/imports/single_file", - pkg: "github.com/wailsapp/wails/exp/pkg/application", - want: []string{"testdata/imports/single_file/main.go"}, - }, - { - name: "should identify multiple files importing package", - dir: "testdata/imports/multiple_files", - pkg: "github.com/wailsapp/wails/exp/pkg/application", - want: []string{"testdata/imports/multiple_files/app.go"}, - }, - { - name: "should identify aliases", - dir: "testdata/imports/alias", - pkg: "github.com/wailsapp/wails/exp/pkg/application", - want: []string{"testdata/imports/alias/main.go"}, - }, - { - name: "should identify packages", - dir: "testdata/imports/other_package", - pkg: "github.com/wailsapp/wails/exp/pkg/application", - want: []string{"testdata/imports/other_package/subpackage/app.go"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := findFilesImportingPackage(tt.dir, tt.pkg) - if (err != nil) != tt.wantErr { - t.Errorf("findFilesImportingPackage() error = %v, wantErr %v", err, tt.wantErr) - return - } - for _, imp := range got { - if !lo.Contains(tt.want, imp.FileName) { - t.Errorf("findFilesImportingPackage() = %v, want %v", got, tt.want) - } - } - }) - } -} diff --git a/exp/internal/parser/options.go b/exp/internal/parser/options.go deleted file mode 100644 index b06122522..000000000 --- a/exp/internal/parser/options.go +++ /dev/null @@ -1,22 +0,0 @@ -package parser - -import ( - "go/ast" -) - -func isOptionsApplication(expr *ast.Expr) bool { - cl, ok := (*expr).(*ast.CompositeLit) - if ok { - se, ok := cl.Type.(*ast.SelectorExpr) - if ok { - _, ok := se.X.(*ast.Ident) - if ok { - if se.Sel.Name == "Application" && se.X.(*ast.Ident).Name == "options" { - return true - } - } - } - } - - return false -} diff --git a/exp/internal/parser/options_test.go b/exp/internal/parser/options_test.go deleted file mode 100644 index c6e47ad0a..000000000 --- a/exp/internal/parser/options_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package parser - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_isOptionsApplication(t *testing.T) { - tests := []struct { - name string - dir string - want bool - }{ - { - name: "should return true when expr is a struct literal", - dir: "testdata/boundstructs/struct_literal_single", - want: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - imports, err := findFilesImportingPackage(tt.dir, "github.com/wailsapp/wails/exp/pkg/application") - require.NoError(t, err) - imp := findNewCalls(imports) - require.NotNil(t, imp) - isOptions := isOptionsApplication(imp.Options) - require.Equal(t, tt.want, isOptions) - }) - } -} diff --git a/exp/internal/parser/packages.go b/exp/internal/parser/packages.go new file mode 100644 index 000000000..28474caf0 --- /dev/null +++ b/exp/internal/parser/packages.go @@ -0,0 +1,268 @@ +package parser + +import ( + "go/ast" + "go/parser" + "go/token" + "strings" + + "github.com/samber/lo" + + "golang.org/x/tools/go/packages" +) + +type parsedPackage struct { + name string + pkg *ast.Package + boundStructs []*ast.TypeSpec +} + +type Context struct { + packages map[string]*parsedPackage +} + +func ParseDirectory(dir string) (*Context, error) { + // Parse the directory + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, dir, nil, parser.AllErrors) + if err != nil { + return nil, err + } + + context := &Context{ + packages: make(map[string]*parsedPackage), + } + + // Iterate through the packages + for _, pkg := range pkgs { + context.packages[pkg.Name] = &parsedPackage{ + name: pkg.Name, + pkg: pkg, + } + } + + findApplicationNewCalls(context) + + return context, nil +} + +func findApplicationNewCalls(context *Context) { + // Iterate through the packages + currentPackages := lo.Keys(context.packages) + + for _, packageName := range currentPackages { + thisPackage := context.packages[packageName] + println("Parsing package", 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 "options.Application" + 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 { + println("Ident.Obj is nil - check") + continue + } + // Check if the ident is a struct type + if _, ok := ident.Obj.Decl.(*ast.TypeSpec); ok { + thisPackage.boundStructs = append(thisPackage.boundStructs, ident.Obj.Decl.(*ast.TypeSpec)) + 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) { + // extract package name from selector + packageName := selector.X.(*ast.Ident).Name + + if context.packages[packageName] == nil { + context.packages[packageName] = &parsedPackage{ + name: packageName, + } + } + + // 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 { + context.packages[packageName].boundStructs = append(context.packages[packageName].boundStructs, typeSpec) + findNestedStructs(typeSpec, parsedFile, packageName, context) + } + } + return true + }) + } + + continue + } + } + +} + +func findNestedStructs(t *ast.TypeSpec, parsedFile *ast.File, pkgName string, context *Context) (localStructs []*ast.TypeSpec, externalStructs []*ast.SelectorExpr) { + structType, ok := t.Type.(*ast.StructType) + if !ok { + return nil, nil + } + 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 { + context.packages[pkgName].boundStructs = append(context.packages[pkgName].boundStructs, t.Obj.Decl.(*ast.TypeSpec)) + } + } + 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) + } + } + } + } + } + } + return localStructs, externalStructs +} diff --git a/exp/internal/parser/packages_test.go b/exp/internal/parser/packages_test.go new file mode 100644 index 000000000..a3c319dd3 --- /dev/null +++ b/exp/internal/parser/packages_test.go @@ -0,0 +1,58 @@ +package parser + +import ( + "testing" + + "github.com/samber/lo" + "github.com/stretchr/testify/require" +) + +func TestParseDirectory(t *testing.T) { + tests := []struct { + name string + dir string + want []string + wantErr bool + }{ + { + name: "should find single bound service", + dir: "testdata/struct_literal_single", + want: []string{"GreetService"}, + wantErr: false, + }, + { + name: "should find multiple bound services", + dir: "testdata/struct_literal_multiple", + want: []string{"main.GreetService", "main.OtherService"}, + wantErr: false, + }, + { + name: "should find multiple bound services over multiple files", + dir: "testdata/struct_literal_multiple_files", + want: []string{"main.GreetService", "main.OtherService"}, + wantErr: false, + }, + { + name: "should find bound services from other packages", + dir: "../../examples/binding", + want: []string{"main.localStruct", "services.GreetService", "models.Person"}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseDirectory(tt.dir) + if (err != nil) != tt.wantErr { + t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr) + return + } + for name, pkg := range got.packages { + println("Got package", name) + for _, boundStruct := range pkg.boundStructs { + println("Got bound struct", boundStruct.Name.Name) + require.True(t, lo.Contains(tt.want, name+"."+boundStruct.Name.Name)) + } + } + }) + } +} diff --git a/exp/internal/parser/testdata/imports/alias/main.go b/exp/internal/parser/testdata/imports/alias/main.go deleted file mode 100644 index 5125b75f6..000000000 --- a/exp/internal/parser/testdata/imports/alias/main.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - _ "embed" - "log" - - bob "github.com/wailsapp/wails/exp/pkg/application" - "github.com/wailsapp/wails/exp/pkg/options" -) - -type GreetService struct { - SomeVariable int - lowerCase string -} - -func (*GreetService) Greet(name string) string { - return "Hello " + name -} - -func main() { - app := bob.New(options.Application{ - Bind: []interface{}{ - &GreetService{}, - }, - }) - - app.NewWebviewWindow() - - err := app.Run() - - if err != nil { - log.Fatal(err) - } - -} diff --git a/exp/internal/parser/testdata/imports/multiple_files/app.go b/exp/internal/parser/testdata/imports/multiple_files/app.go deleted file mode 100644 index 0d1802b3d..000000000 --- a/exp/internal/parser/testdata/imports/multiple_files/app.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - _ "embed" - "log" - - "github.com/wailsapp/wails/exp/pkg/application" - "github.com/wailsapp/wails/exp/pkg/options" -) - -type GreetService struct { - SomeVariable int - lowerCase string -} - -func (*GreetService) Greet(name string) string { - return "Hello " + name -} - -func run() { - app := application.New(options.Application{ - Bind: []interface{}{ - &GreetService{}, - }, - }) - - app.NewWebviewWindow() - - err := app.Run() - - if err != nil { - log.Fatal(err) - } - -} diff --git a/exp/internal/parser/testdata/imports/multiple_files/main.go b/exp/internal/parser/testdata/imports/multiple_files/main.go deleted file mode 100644 index c0aced91b..000000000 --- a/exp/internal/parser/testdata/imports/multiple_files/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - _ "embed" -) - -func main() { - run() -} diff --git a/exp/internal/parser/testdata/imports/other_package/go.mod b/exp/internal/parser/testdata/imports/other_package/go.mod deleted file mode 100644 index 4058e72ba..000000000 --- a/exp/internal/parser/testdata/imports/other_package/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module other_package - -require github.com/wailsapp/wails/exp v0 - -go 1.19 - -replace github.com/wailsapp/wails/exp v0 => ../../../../../../ \ No newline at end of file diff --git a/exp/internal/parser/testdata/imports/other_package/main.go b/exp/internal/parser/testdata/imports/other_package/main.go deleted file mode 100644 index bb616d756..000000000 --- a/exp/internal/parser/testdata/imports/other_package/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "other_package/subpackage" -) - -func main() { - subpackage.Run() -} diff --git a/exp/internal/parser/testdata/imports/other_package/subpackage/app.go b/exp/internal/parser/testdata/imports/other_package/subpackage/app.go deleted file mode 100644 index 4c4c596fe..000000000 --- a/exp/internal/parser/testdata/imports/other_package/subpackage/app.go +++ /dev/null @@ -1,35 +0,0 @@ -package subpackage - -import ( - _ "embed" - "log" - - "github.com/wailsapp/wails/exp/pkg/application" - "github.com/wailsapp/wails/exp/pkg/options" -) - -type GreetService struct { - SomeVariable int - lowerCase string -} - -func (*GreetService) Greet(name string) string { - return "Hello " + name -} - -func Run() { - app := application.New(options.Application{ - Bind: []interface{}{ - &GreetService{}, - }, - }) - - app.NewWebviewWindow() - - err := app.Run() - - if err != nil { - log.Fatal(err) - } - -} diff --git a/exp/internal/parser/testdata/imports/single_file/main.go b/exp/internal/parser/testdata/imports/single_file/main.go deleted file mode 100644 index 4fb639008..000000000 --- a/exp/internal/parser/testdata/imports/single_file/main.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - _ "embed" - "log" - - "github.com/wailsapp/wails/exp/pkg/application" - "github.com/wailsapp/wails/exp/pkg/options" -) - -type GreetService struct { - SomeVariable int - lowerCase string -} - -func (*GreetService) Greet(name string) string { - return "Hello " + name -} - -func main() { - app := application.New(options.Application{ - Bind: []interface{}{ - &GreetService{}, - }, - }) - - app.NewWebviewWindow() - - err := app.Run() - - if err != nil { - log.Fatal(err) - } - -} diff --git a/exp/internal/parser/testdata/boundstructs/struct_literal_multiple/main.go b/exp/internal/parser/testdata/struct_literal_multiple/main.go similarity index 100% rename from exp/internal/parser/testdata/boundstructs/struct_literal_multiple/main.go rename to exp/internal/parser/testdata/struct_literal_multiple/main.go diff --git a/exp/internal/parser/testdata/boundstructs/struct_literal_multiple_files/greet.go b/exp/internal/parser/testdata/struct_literal_multiple_files/greet.go similarity index 100% rename from exp/internal/parser/testdata/boundstructs/struct_literal_multiple_files/greet.go rename to exp/internal/parser/testdata/struct_literal_multiple_files/greet.go diff --git a/exp/internal/parser/testdata/boundstructs/struct_literal_multiple_files/main.go b/exp/internal/parser/testdata/struct_literal_multiple_files/main.go similarity index 100% rename from exp/internal/parser/testdata/boundstructs/struct_literal_multiple_files/main.go rename to exp/internal/parser/testdata/struct_literal_multiple_files/main.go diff --git a/exp/internal/parser/testdata/boundstructs/struct_literal_multiple_files/other.go b/exp/internal/parser/testdata/struct_literal_multiple_files/other.go similarity index 100% rename from exp/internal/parser/testdata/boundstructs/struct_literal_multiple_files/other.go rename to exp/internal/parser/testdata/struct_literal_multiple_files/other.go diff --git a/exp/internal/parser/testdata/boundstructs/struct_literal_single/main.go b/exp/internal/parser/testdata/struct_literal_single/main.go similarity index 100% rename from exp/internal/parser/testdata/boundstructs/struct_literal_single/main.go rename to exp/internal/parser/testdata/struct_literal_single/main.go