mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-03 04:11:05 +08:00
Support references to structs in other packages
This commit is contained in:
parent
8fd0e06c24
commit
cd11c0a83c
@ -6,6 +6,7 @@ import (
|
|||||||
"go/build"
|
"go/build"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -29,6 +30,7 @@ type ParameterType struct {
|
|||||||
IsPointer bool
|
IsPointer bool
|
||||||
MapKey *ParameterType
|
MapKey *ParameterType
|
||||||
MapValue *ParameterType
|
MapValue *ParameterType
|
||||||
|
Package string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Parameter struct {
|
type Parameter struct {
|
||||||
@ -59,6 +61,7 @@ type ParsedPackage struct {
|
|||||||
type Project struct {
|
type Project struct {
|
||||||
Path string
|
Path string
|
||||||
BoundMethods map[packagePath]map[structName][]*BoundMethod
|
BoundMethods map[packagePath]map[structName][]*BoundMethod
|
||||||
|
Models map[packagePath]map[structName]*StructDef
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseProject(projectPath string) (*Project, error) {
|
func ParseProject(projectPath string) (*Project, error) {
|
||||||
@ -74,13 +77,19 @@ func ParseProject(projectPath string) (*Project, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
for _, pkg := range packageCache {
|
||||||
|
if len(pkg.StructCache) > 0 {
|
||||||
|
if result.Models == nil {
|
||||||
|
result.Models = make(map[packagePath]map[structName]*StructDef)
|
||||||
|
}
|
||||||
|
result.Models[pkg.Path] = pkg.StructCache
|
||||||
|
}
|
||||||
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error) {
|
func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error) {
|
||||||
println("Parsing directory " + dir)
|
|
||||||
if packageCache[dir] != nil {
|
if packageCache[dir] != nil {
|
||||||
println("Found directory in cache!")
|
|
||||||
return map[string]*ParsedPackage{dir: packageCache[dir]}, nil
|
return map[string]*ParsedPackage{dir: packageCache[dir]}, nil
|
||||||
}
|
}
|
||||||
// Parse the directory
|
// Parse the directory
|
||||||
@ -105,7 +114,7 @@ func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error)
|
|||||||
Dir: getDirectoryForPackage(pkg),
|
Dir: getDirectoryForPackage(pkg),
|
||||||
StructCache: make(map[structName]*StructDef),
|
StructCache: make(map[structName]*StructDef),
|
||||||
}
|
}
|
||||||
packageCache[dir] = parsedPackage
|
packageCache[packageName] = parsedPackage
|
||||||
result[packageName] = parsedPackage
|
result[packageName] = parsedPackage
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
@ -115,9 +124,8 @@ func (p *Project) findApplicationNewCalls(pkgs map[string]*ParsedPackage) (err e
|
|||||||
|
|
||||||
var callFound bool
|
var callFound bool
|
||||||
|
|
||||||
for packageName, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
thisPackage := pkg.Pkg
|
thisPackage := pkg.Pkg
|
||||||
println(" - Looking in package: " + packageName)
|
|
||||||
// Iterate through the package's files
|
// Iterate through the package's files
|
||||||
for _, file := range thisPackage.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
|
||||||
@ -338,12 +346,20 @@ func (p *Project) parseParameterType(field *ast.Field, pkg *ParsedPackage) *Para
|
|||||||
result := &ParameterType{}
|
result := &ParameterType{}
|
||||||
result.Name = getTypeString(field.Type)
|
result.Name = getTypeString(field.Type)
|
||||||
switch t := field.Type.(type) {
|
switch t := field.Type.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
result.IsStruct = isStructType(t)
|
||||||
case *ast.StarExpr:
|
case *ast.StarExpr:
|
||||||
result = p.parseParameterType(&ast.Field{Type: t.X}, pkg)
|
result = p.parseParameterType(&ast.Field{Type: t.X}, pkg)
|
||||||
result.IsPointer = true
|
result.IsPointer = true
|
||||||
result.IsStruct = isStructType(t.X)
|
|
||||||
case *ast.StructType:
|
case *ast.StructType:
|
||||||
result.IsStruct = true
|
result.IsStruct = true
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
extPackage, err := p.getParsedPackageFromName(t.X.(*ast.Ident).Name, pkg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
result.IsStruct = p.getStructDef(t.Sel.Name, extPackage)
|
||||||
|
result.Package = extPackage.Path
|
||||||
case *ast.ArrayType:
|
case *ast.ArrayType:
|
||||||
result.IsSlice = true
|
result.IsSlice = true
|
||||||
result.IsStruct = isStructType(t.Elt)
|
result.IsStruct = isStructType(t.Elt)
|
||||||
@ -355,18 +371,18 @@ func (p *Project) parseParameterType(field *ast.Field, pkg *ParsedPackage) *Para
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
if result.IsStruct {
|
if result.IsStruct {
|
||||||
_, ok := pkg.StructCache[result.Name]
|
p.getStructDef(result.Name, pkg)
|
||||||
if !ok {
|
if result.Package == "" {
|
||||||
p.getStructDef(result.Name, pkg)
|
result.Package = pkg.Path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Project) getStructDef(name string, pkg *ParsedPackage) {
|
func (p *Project) getStructDef(name string, pkg *ParsedPackage) bool {
|
||||||
_, ok := pkg.StructCache[name]
|
_, ok := pkg.StructCache[name]
|
||||||
if ok {
|
if ok {
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
// Iterate over all files in the package
|
// Iterate over all files in the package
|
||||||
for _, file := range pkg.Pkg.Files {
|
for _, file := range pkg.Pkg.Files {
|
||||||
@ -386,6 +402,7 @@ func (p *Project) getStructDef(name string, pkg *ParsedPackage) {
|
|||||||
}
|
}
|
||||||
pkg.StructCache[name] = result
|
pkg.StructCache[name] = result
|
||||||
result.Fields = p.parseStructFields(structType, pkg)
|
result.Fields = p.parseStructFields(structType, pkg)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -394,6 +411,7 @@ func (p *Project) getStructDef(name string, pkg *ParsedPackage) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Project) parseStructFields(structType *ast.StructType, pkg *ParsedPackage) []*Field {
|
func (p *Project) parseStructFields(structType *ast.StructType, pkg *ParsedPackage) []*Field {
|
||||||
@ -419,6 +437,9 @@ func (p *Project) parseStructFields(structType *ast.StructType, pkg *ParsedPacka
|
|||||||
if !ok {
|
if !ok {
|
||||||
p.getStructDef(paramType.Name, pkg)
|
p.getStructDef(paramType.Name, pkg)
|
||||||
}
|
}
|
||||||
|
if paramType.Package == "" {
|
||||||
|
paramType.Package = pkg.Path
|
||||||
|
}
|
||||||
}
|
}
|
||||||
thisField.Type = paramType
|
thisField.Type = paramType
|
||||||
result = append(result, thisField)
|
result = append(result, thisField)
|
||||||
@ -445,13 +466,15 @@ func (p *Project) getParsedPackageFromName(packageName string, currentPackage *P
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &ParsedPackage{
|
result := &ParsedPackage{
|
||||||
Pkg: pkg,
|
Pkg: pkg,
|
||||||
Name: packageName,
|
Name: packageName,
|
||||||
Path: path,
|
Path: path,
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
StructCache: make(map[string]*StructDef),
|
StructCache: make(map[string]*StructDef),
|
||||||
}, nil
|
}
|
||||||
|
packageCache[path] = result
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -490,6 +513,8 @@ func getTypeString(expr ast.Expr) string {
|
|||||||
return getTypeString(t.Elt)
|
return getTypeString(t.Elt)
|
||||||
case *ast.MapType:
|
case *ast.MapType:
|
||||||
return "map"
|
return "map"
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
return getTypeString(t.Sel)
|
||||||
default:
|
default:
|
||||||
return "any"
|
return "any"
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
dir string
|
dir string
|
||||||
wantBoundMethods map[string]map[string][]*BoundMethod
|
wantBoundMethods map[string]map[string][]*BoundMethod
|
||||||
|
wantModels map[string]map[string]*StructDef
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@ -689,6 +690,7 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
Name: "Person",
|
Name: "Person",
|
||||||
IsPointer: true,
|
IsPointer: true,
|
||||||
IsStruct: true,
|
IsStruct: true,
|
||||||
|
Package: "main",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -709,6 +711,7 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
Name: "Person",
|
Name: "Person",
|
||||||
IsPointer: true,
|
IsPointer: true,
|
||||||
IsStruct: true,
|
IsStruct: true,
|
||||||
|
Package: "main",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -718,6 +721,7 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
Name: "Person",
|
Name: "Person",
|
||||||
IsPointer: true,
|
IsPointer: true,
|
||||||
IsStruct: true,
|
IsStruct: true,
|
||||||
|
Package: "main",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -841,6 +845,30 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
wantModels: map[string]map[string]*StructDef{
|
||||||
|
"main": {
|
||||||
|
"Person": {
|
||||||
|
Name: "Person",
|
||||||
|
Fields: []*Field{
|
||||||
|
{
|
||||||
|
Name: "Name",
|
||||||
|
Type: &ParameterType{
|
||||||
|
Name: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Parent",
|
||||||
|
Type: &ParameterType{
|
||||||
|
Name: "Person",
|
||||||
|
IsStruct: true,
|
||||||
|
IsPointer: true,
|
||||||
|
Package: "main",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -960,6 +988,7 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
Name: "Person",
|
Name: "Person",
|
||||||
IsPointer: true,
|
IsPointer: true,
|
||||||
IsStruct: true,
|
IsStruct: true,
|
||||||
|
Package: "main",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -973,8 +1002,10 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
Outputs: []*Parameter{
|
Outputs: []*Parameter{
|
||||||
{
|
{
|
||||||
Type: &ParameterType{
|
Type: &ParameterType{
|
||||||
Name: "int",
|
Name: "Address",
|
||||||
IsSlice: true,
|
IsStruct: true,
|
||||||
|
IsPointer: true,
|
||||||
|
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -982,6 +1013,55 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
wantModels: map[string]map[string]*StructDef{
|
||||||
|
"main": {
|
||||||
|
"Person": {
|
||||||
|
Name: "Person",
|
||||||
|
Fields: []*Field{
|
||||||
|
{
|
||||||
|
Name: "Name",
|
||||||
|
Type: &ParameterType{
|
||||||
|
Name: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Address",
|
||||||
|
Type: &ParameterType{
|
||||||
|
Name: "Address",
|
||||||
|
IsStruct: true,
|
||||||
|
IsPointer: true,
|
||||||
|
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services": {
|
||||||
|
"Address": {
|
||||||
|
Name: "Address",
|
||||||
|
Fields: []*Field{
|
||||||
|
{
|
||||||
|
Name: "Street",
|
||||||
|
Type: &ParameterType{
|
||||||
|
Name: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "State",
|
||||||
|
Type: &ParameterType{
|
||||||
|
Name: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Country",
|
||||||
|
Type: &ParameterType{
|
||||||
|
Name: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -994,6 +1074,9 @@ func TestParseDirectory(t *testing.T) {
|
|||||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||||
}
|
}
|
||||||
|
if diff := cmp.Diff(tt.wantModels, got.Models); diff != "" {
|
||||||
|
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -16,7 +16,8 @@ type GreetService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Person struct {
|
type Person struct {
|
||||||
Name string
|
Name string
|
||||||
|
Address *services.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
// Greet does XYZ
|
// Greet does XYZ
|
||||||
|
@ -6,7 +6,17 @@ type OtherService struct {
|
|||||||
t int
|
t int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yay does this and that
|
type Address struct {
|
||||||
func (o *OtherService) Yay() []int {
|
Street string
|
||||||
return []int{0, 1, 2}
|
State string
|
||||||
|
Country string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yay does this and that
|
||||||
|
func (o *OtherService) Yay() *Address {
|
||||||
|
return &Address{
|
||||||
|
Street: "123 Pitt Street",
|
||||||
|
State: "New South Wales",
|
||||||
|
Country: "Australia",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user