5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 03:01:45 +08:00

Fix differeng bugs when generating bindings (#2326)

* Bindings: work in better typescript generation

* More work on generating typescript

* Bindings: fix a couple of failing tests

* Binding: fix map bindings

* Bindings: comment out debug statement

* Bindings: misc cleanup
This commit is contained in:
Adam Tenderholt 2023-01-25 23:40:05 -08:00 committed by GitHub
parent 180eef34a2
commit 88549a14ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 145 additions and 35 deletions

View File

@ -0,0 +1,64 @@
package binding_test
import (
"github.com/wailsapp/wails/v2/internal/binding/binding_test/binding_test_import/int_package"
"io/fs"
"os"
"testing"
"github.com/wailsapp/wails/v2/internal/binding"
"github.com/wailsapp/wails/v2/internal/logger"
)
const expectedTypeAliasBindings = `// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {binding_test} from '../models';
import {int_package} from '../models';
export function Map():Promise<{[key: string]: string}>;
export function MapAlias():Promise<binding_test.MapAlias>;
export function MapWithImportedStructValue():Promise<{[key: string]: int_package.SomeStruct}>;
export function Slice():Promise<Array<string>>;
export function SliceImportedStruct():Promise<Array<int_package.SomeStruct>>;
`
type AliasTest struct{}
type MapAlias map[string]string
func (h *AliasTest) Map() map[string]string { return nil }
func (h *AliasTest) MapAlias() MapAlias { return nil }
func (h *AliasTest) MapWithImportedStructValue() map[string]int_package.SomeStruct { return nil }
func (h *AliasTest) Slice() []string { return nil }
func (h *AliasTest) SliceImportedStruct() []int_package.SomeStruct { return nil }
func TestAliases(t *testing.T) {
// given
generationDir := t.TempDir()
// setup
testLogger := &logger.Logger{}
b := binding.NewBindings(testLogger, []interface{}{&AliasTest{}}, []interface{}{}, false)
// then
err := b.GenerateGoBindings(generationDir)
if err != nil {
t.Fatalf("could not generate the Go bindings: %v", err)
}
// then
rawGeneratedBindings, err := fs.ReadFile(os.DirFS(generationDir), "binding_test/AliasTest.d.ts")
if err != nil {
t.Fatalf("could not read the generated bindings: %v", err)
}
// then
generatedBindings := string(rawGeneratedBindings)
if generatedBindings != expectedTypeAliasBindings {
t.Fatalf("the generated bindings does not match the expected ones.\nWanted:\n%s\n\nGot:\n%s", expectedTypeAliasBindings,
generatedBindings)
}
}

View File

@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
@ -14,6 +15,22 @@ import (
"github.com/leaanthony/slicer"
)
var mapRegex *regexp.Regexp
var keyPackageIndex int
var keyTypeIndex int
var valueArrayIndex int
var valuePackageIndex int
var valueTypeIndex int
func init() {
mapRegex = regexp.MustCompile(`(?:map\[(?:(?P<keyPackage>\w+)\.)?(?P<keyType>\w+)])?(?P<valueArray>\[])?(?:(?P<valuePackage>\w+)\.)?(?P<valueType>.+)`)
keyPackageIndex = mapRegex.SubexpIndex("keyPackage")
keyTypeIndex = mapRegex.SubexpIndex("keyType")
valueArrayIndex = mapRegex.SubexpIndex("valueArray")
valuePackageIndex = mapRegex.SubexpIndex("valuePackage")
valueTypeIndex = mapRegex.SubexpIndex("valueType")
}
func (b *Bindings) GenerateGoBindings(baseDir string) error {
store := b.db.store
var obfuscatedBindings map[string]int
@ -128,56 +145,85 @@ func (b *Bindings) GenerateGoBindings(baseDir string) error {
return nil
}
func goTypeToJSDocType(input string, importNamespaces *slicer.StringSlicer) string {
// Verifying this first to ensure we are not converting a type
// coming from a package that has a name matching a golang type, such as:
// - interactor -> int
// - mapper -> map
if strings.ContainsRune(input, '.') {
namespace := getPackageName(input)
importNamespaces.Add(namespace)
return namespace + "." + strings.Split(input, ".")[1]
func fullyQualifiedName(packageName string, typeName string) string {
if len(packageName) > 0 {
return packageName + "." + typeName
}
switch true {
case input == "interface {}" || input == "interface{}":
case len(typeName) == 0:
return ""
case typeName == "interface{}" || typeName == "interface {}":
return "any"
case input == "string":
case typeName == "string":
return "string"
case input == "error":
case typeName == "error":
return "Error"
case
strings.HasPrefix(input, "int"),
strings.HasPrefix(input, "uint"),
strings.HasPrefix(input, "float"):
strings.HasPrefix(typeName, "int"),
strings.HasPrefix(typeName, "uint"),
strings.HasPrefix(typeName, "float"):
return "number"
case input == "bool":
case typeName == "bool":
return "boolean"
case input == "[]byte":
return "string"
case strings.HasPrefix(input, "map"):
temp := strings.TrimPrefix(input, "map[")
// Split the string into the key and value types
tempSplit := strings.SplitN(temp, "]", 2)
if len(tempSplit) < 2 {
panic("Invalid map type provided: " + input)
}
keyType := tempSplit[0]
valueType := tempSplit[1]
return fmt.Sprintf("{[key: %s]: %s}", goTypeToJSDocType(keyType, importNamespaces), goTypeToJSDocType(valueType, importNamespaces))
case strings.HasPrefix(input, "[]"):
arrayType := goTypeToJSDocType(input[2:], importNamespaces)
return "Array<" + arrayType + ">"
default:
return "any"
}
}
func goTypeToTypescriptType(input string, importNamespaces *slicer.StringSlicer) string {
if strings.HasPrefix(input, "[]") {
arrayType := goTypeToJSDocType(input[2:], importNamespaces)
return "Array<" + arrayType + ">"
func arrayifyValue(valueArray string, valueType string) string {
if len(valueArray) == 0 {
return valueType
}
return "Array<" + valueType + ">"
}
func goTypeToJSDocType(input string, importNamespaces *slicer.StringSlicer) string {
matches := mapRegex.FindStringSubmatch(input)
keyPackage := matches[keyPackageIndex]
keyType := matches[keyTypeIndex]
valueArray := matches[valueArrayIndex]
valuePackage := matches[valuePackageIndex]
valueType := matches[valueTypeIndex]
//fmt.Printf("input=%s, keyPackage=%s, keyType=%s, valueArray=%s, valuePackage=%s, valueType=%s\n",
// input,
// keyPackage,
// keyType,
// valueArray,
// valuePackage,
// valueType)
// byte array is special case
if valueArray == "[]" && valueType == "byte" {
return "string"
}
// if any packages, make sure they're saved
if len(keyPackage) > 0 {
importNamespaces.Add(keyPackage)
}
if len(valuePackage) > 0 {
importNamespaces.Add(valuePackage)
}
key := fullyQualifiedName(keyPackage, keyType)
var value string
if strings.HasPrefix(valueType, "map") {
value = goTypeToJSDocType(valueType, importNamespaces)
} else {
value = fullyQualifiedName(valuePackage, valueType)
}
if len(key) > 0 {
return fmt.Sprintf("{[key: %s]: %s}", key, arrayifyValue(valueArray, value))
}
return arrayifyValue(valueArray, value)
}
func goTypeToTypescriptType(input string, importNamespaces *slicer.StringSlicer) string {
return goTypeToJSDocType(input, importNamespaces)
}