Skip to content

Commit

Permalink
Update bindings generator to generate bindings in packages and files.
Browse files Browse the repository at this point in the history
Remove unused JavaScript files
Update tests.
Update v3 docs
  • Loading branch information
leaanthony committed Dec 22, 2023
1 parent cdf4bdd commit 2bb25b1
Show file tree
Hide file tree
Showing 125 changed files with 3,963 additions and 2,113 deletions.
41 changes: 8 additions & 33 deletions mkdocs-website/docs/en/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,16 @@ We also want to get all examples working on Linux.

#### How Can I Help?

If you are interested in helping out, please review the table below and look for
untested scenarios. The parser code and tests are located in `v3/internal/parser`.
All tests can be run using `go test ./...` from the `v3` directory.
You can generate bindings using the `wails3 generate bindings` command. This will generate bindings for all exported struct methods bound to your project.
You can then use these bindings in your frontend code. You can see an example of this in the `examples/bindings` directory.
Run `wails3 generate bindings -help` to view options that govern how bindings are generated.

Review the table below and look for untested scenarios. The parser code and tests are located in `v3/internal/parser`. All tests can be run using `go test ./...` from the `v3` directory.
Basically, try to break it and let us know if you find any issues! :smile:

#### Status

Bindings for struct (CallByID):
- [x] Same package
- [x] Different package
- [ ] Different package with same name
- [x] Containing another struct from same package
- [x] Containing another struct from different package
- [x] Containing an anonymous struct


- :material-check-bold: - Working
- :material-minus: - Partially working
Expand All @@ -40,12 +35,6 @@ Bindings for struct (CallByID):
{{ read_csv("alpha3-bindings-callbyid.csv") }}

Bindings for struct (CallByName):
- [ ] Same package
- [ ] Different package
- [ ] Different package with same name
- [ ] Containing another struct from same package
- [ ] Containing another struct from different package
- [ ] Containing an anonymous struct

- :material-check-bold: - Working
- :material-minus: - Partially working
Expand All @@ -54,31 +43,17 @@ Bindings for struct (CallByName):
{{ read_csv("alpha3-bindings-callbyname.csv") }}

Models:
- [x] Class model for struct in same package
- [x] Class model for struct in different package
- [ ] Interface model for struct in same package
- [ ] Interface model for struct in different package
- [x] Enum in same package
- [x] Enum in different package
- [x] Interface using enum in same package
- [ ] Interface using enum in different package

Examples:
- [ ] All examples working on Linux


- :material-check-bold: - Working
- :material-minus: - Partially working
- :material-close: - Not working

**Bindings**:

{{ read_csv("alpha3-bindings-callbyid.csv") }}
{{ read_csv("alpha3-models.csv") }}

**Models**:

{{ read_csv("alpha3-models.csv") }}
Examples:

- [ ] All examples working on Linux


### Alpha 2
Expand Down
10 changes: 5 additions & 5 deletions mkdocs-website/shared/alpha3-bindings-callbyname.csv
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Scenario,Windows,Mac,Linux,WSL
Same package," "," "," "," "
Different package," "," "," "," "
Same package,:material-check-bold:," "," "," "
Different package,:material-check-bold:," "," "," "
Different package with same name," "," "," "," "
Containing another struct from same package," "," "," "," "
Containing another struct from different package," "," "," "," "
Containing an anonymous struct," "," "," "," "
Containing another struct from same package,:material-check-bold:," "," "," "
Containing another struct from different package,:material-check-bold:," "," "," "
Containing an anonymous struct,:material-check-bold:," "," "," "
6 changes: 3 additions & 3 deletions mkdocs-website/shared/alpha3-models.csv
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Scenario,Windows,Mac,Linux,WSL
Class model for struct in same package,:material-check-bold:," "," "," "
Class model for struct in different package,:material-check-bold:," "," "," "
Interface model for struct in same package," "," "," "," "
Interface model for struct in different package," "," "," "," "
Interface model for struct in same package,:material-check-bold:," "," "," "
Interface model for struct in different package,:material-check-bold:," "," "," "
Enum in same package,:material-check-bold:," "," "," "
Enum in different package,:material-check-bold:," "," "," "
Interface using enum in same package,:material-check-bold:," "," "," "
Interface using enum in different package," "," "," "," "
Interface using enum in different package,:material-check-bold:," "," "," "

1 change: 1 addition & 0 deletions v3/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ toolchain go1.21.4
require (
github.com/atterpac/refresh v0.2.4
github.com/bep/debounce v1.2.1
github.com/davecgh/go-spew v1.1.1
github.com/ebitengine/purego v0.4.0-alpha.4
github.com/go-git/go-git/v5 v5.3.0
github.com/go-ole/go-ole v1.2.6
Expand Down
142 changes: 98 additions & 44 deletions v3/internal/parser/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,26 @@ const header = `// @ts-check
`

const bindingTemplate = `
/**
* {{structName}}.{{methodName}}
*Comments
* @param names {string}
* @returns {Promise<string>}
**/
/**Comments
* @function {{methodName}}* @param names {string}
* @returns {Promise<string>}
**/
`

const callByID = ` {{methodName}}: function({{inputs}}) { return wails.CallByID({{ID}}, ...Array.prototype.slice.call(arguments, 0)); },
const callByID = `export function {{methodName}}({{inputs}}) {
return wails.CallByID({{ID}}, ...Array.prototype.slice.call(arguments, 0));
}
`
const callByName = ` {{methodName}}: function({{inputs}}) { return wails.CallByName("{{Name}}", ...Array.prototype.slice.call(arguments, 0)); },

const callByName = `export function {{methodName}}({{inputs}}) {
return wails.CallByName("{{Name}}", ...Array.prototype.slice.call(arguments, 0));
}
`

const enumTemplate = `
export enum {{.EnumName}} {
{{.EnumValues}}
}
export enum {{.EnumName}} {
{{.EnumValues}}
}
`

var reservedWords = []string{
Expand Down Expand Up @@ -112,27 +115,32 @@ func sanitiseJSVarName(name string) string {
return name
}

func GenerateBinding(structName string, method *BoundMethod, useIDs bool) (string, []string, []string) {
var namespacedStructs []string
type ExternalStruct struct {
Package string
Name string
}

func GenerateBinding(thisStructName string, method *BoundMethod, useIDs bool) (string, []string, map[packagePath]map[string]*ExternalStruct) {
var externalStructs = make(map[packagePath]map[string]*ExternalStruct)
var models []string
template := bindingTemplate
if useIDs {
template += callByID
} else {
template += callByName
}
result := strings.ReplaceAll(template, "{{structName}}", structName)
result := strings.ReplaceAll(template, "{{structName}}", thisStructName)
result = strings.ReplaceAll(result, "{{methodName}}", method.Name)
result = strings.ReplaceAll(result, "{{ID}}", fmt.Sprintf("%v", method.ID))

// get last part of method.Package path
parts := strings.Split(method.Package, "/")
packageName := parts[len(parts)-1]

result = strings.ReplaceAll(result, "{{Name}}", fmt.Sprintf("%v.%v.%v", packageName, structName, method.Name))
result = strings.ReplaceAll(result, "{{Name}}", fmt.Sprintf("%v.%v.%v", packageName, thisStructName, method.Name))
comments := strings.TrimSpace(method.DocComment)
if comments != "" {
comments = " " + comments
comments = "\n * " + comments
}
result = strings.ReplaceAll(result, "Comments", comments)
var params string
Expand All @@ -143,16 +151,22 @@ func GenerateBinding(structName string, method *BoundMethod, useIDs bool) (strin
models = append(models, pkgName)
}
if input.Type.IsStruct || input.Type.IsEnum {
nsStruct := input.NamespacedStructType()
namespacedStructs = append(namespacedStructs, nsStruct)
if _, ok := externalStructs[input.Type.Package]; !ok {
externalStructs[input.Type.Package] = make(map[string]*ExternalStruct)
}
externalStructs[input.Type.Package][input.Type.Name] = &ExternalStruct{
Package: input.Type.Package,
Name: input.Type.Name,
}
}

params += " * @param " + inputName + " {" + input.JSType() + "}\n"
inputType := input.JSType(packageName)
params += "\n * @param " + inputName + " {" + inputType + "}"
}
params = strings.TrimSuffix(params, "\n")
if len(params) == 0 {
params = " *"
}
//if len(params) > 0 {
// params = "\n" + params
//}
result = strings.ReplaceAll(result, "* @param names {string}", params)
var inputs string
for _, input := range method.Inputs {
Expand Down Expand Up @@ -181,13 +195,19 @@ func GenerateBinding(structName string, method *BoundMethod, useIDs bool) (strin
if pkgName != "" {
models = append(models, pkgName)
}
jsType := output.JSType()
jsType := output.JSType(packageName)
if jsType == "error" {
jsType = "void"
}
if output.Type.IsStruct {
namespacedStructs = append(namespacedStructs, output.NamespacedStructType())
jsType = output.NamespacedStructVariable()
if _, ok := externalStructs[output.Type.Package]; !ok {
externalStructs[output.Type.Package] = make(map[string]*ExternalStruct)
}
externalStructs[output.Type.Package][output.Type.Name] = &ExternalStruct{
Package: output.Type.Package,
Name: output.Type.Name,
}
jsType = output.NamespacedStructVariable(output.Type.Package)
}
returns += jsType + ", "
}
Expand All @@ -196,7 +216,7 @@ func GenerateBinding(structName string, method *BoundMethod, useIDs bool) (strin
}
result = strings.ReplaceAll(result, " * @returns {Promise<string>}", returns)

return result, lo.Uniq(models), lo.Uniq(namespacedStructs)
return result, lo.Uniq(models), externalStructs
}

func getPackageName(input *Parameter) string {
Expand Down Expand Up @@ -240,47 +260,81 @@ func normalisePackageNames(packageNames []string) map[string]string {
return result
}

func GenerateBindings(bindings map[string]map[string][]*BoundMethod, useIDs bool) map[string]string {
func (p *Project) GenerateBindings(bindings map[string]map[string][]*BoundMethod, useIDs bool) map[string]map[string]string {

var result = make(map[string]string)
var result = make(map[string]map[string]string)

var normalisedPackageNames = normalisePackageNames(lo.Keys(bindings))
// sort the bindings keys
packageNames := lo.Keys(bindings)
sort.Strings(packageNames)
for _, packageName := range packageNames {
var allModels []string
var allNamespacedStructs []string

packageBindings := bindings[packageName]
structNames := lo.Keys(packageBindings)
relativePackageDir := p.RelativePackageDir(packageName)
_ = relativePackageDir
sort.Strings(structNames)
for _, structName := range structNames {
result[normalisedPackageNames[packageName]] += "export const " + structName + " = {\n"
if _, ok := result[relativePackageDir]; !ok {
result[relativePackageDir] = make(map[string]string)
}
methods := packageBindings[structName]
sort.Slice(methods, func(i, j int) bool {
return methods[i].Name < methods[j].Name
})
var allNamespacedStructs map[packagePath]map[string]*ExternalStruct
var namespacedStructs map[packagePath]map[string]*ExternalStruct
var thisBinding string
var models []string
for _, method := range methods {
thisBinding, models, namespacedStructs := GenerateBinding(structName, method, useIDs)
allNamespacedStructs = append(allNamespacedStructs, namespacedStructs...)
thisBinding, models, namespacedStructs = GenerateBinding(structName, method, useIDs)
// Merge the namespaced structs
allNamespacedStructs = mergeNamespacedStructs(allNamespacedStructs, namespacedStructs)
allModels = append(allModels, models...)
result[normalisedPackageNames[packageName]] += thisBinding
result[relativePackageDir][structName] += thisBinding
}
result[normalisedPackageNames[packageName]] += "};\n\n"
}

if len(allNamespacedStructs) > 0 {
typedefs := "/**\n"
for _, namespacedStruct := range lo.Uniq(allNamespacedStructs) {
typedefs += " * @typedef {import('./models')." + namespacedStruct + "} " + strings.ReplaceAll(namespacedStruct, ".", "") + "\n"
if len(allNamespacedStructs) > 0 {
thisPkg := p.packageCache[packageName]
typedefs := "/**\n"
for externalPackageName, namespacedStruct := range allNamespacedStructs {
pkgInfo := p.packageCache[externalPackageName]
relativePackageDir := p.RelativeBindingsDir(thisPkg, pkgInfo)
namePrefix := ""
if pkgInfo.Name != "" && pkgInfo.Path != thisPkg.Path {
namePrefix = pkgInfo.Name
}

// Get keys from namespacedStruct and iterate over them in sorted order
namespacedStructNames := lo.Keys(namespacedStruct)
sort.Strings(namespacedStructNames)
for _, thisStructName := range namespacedStructNames {
structInfo := namespacedStruct[thisStructName]
typedefs += " * @typedef {import('" + relativePackageDir + "/models')." + thisStructName + "} " + namePrefix + structInfo.Name + "\n"
}
}
typedefs += " */\n"
result[relativePackageDir][structName] = typedefs + result[relativePackageDir][structName]
}
typedefs += " */\n\n"
result[normalisedPackageNames[packageName]] = typedefs + result[normalisedPackageNames[packageName]]
result[relativePackageDir][structName] = header + result[relativePackageDir][structName]
}

result[normalisedPackageNames[packageName]] = header + result[normalisedPackageNames[packageName]]
}

return result
}

func mergeNamespacedStructs(structs map[packagePath]map[string]*ExternalStruct, structs2 map[packagePath]map[string]*ExternalStruct) map[packagePath]map[string]*ExternalStruct {
if structs == nil {
structs = make(map[packagePath]map[string]*ExternalStruct)
}
for pkg, pkgStructs := range structs2 {
if _, ok := structs[pkg]; !ok {
structs[pkg] = make(map[string]*ExternalStruct)
}
for name, structInfo := range pkgStructs {
structs[pkg][name] = structInfo
}
}
return structs
}
Loading

0 comments on commit 2bb25b1

Please sign in to comment.