5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-03 01:43:15 +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" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"sort" "sort"
"strings" "strings"
@ -14,6 +15,22 @@ import (
"github.com/leaanthony/slicer" "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 { func (b *Bindings) GenerateGoBindings(baseDir string) error {
store := b.db.store store := b.db.store
var obfuscatedBindings map[string]int var obfuscatedBindings map[string]int
@ -128,56 +145,85 @@ func (b *Bindings) GenerateGoBindings(baseDir string) error {
return nil return nil
} }
func goTypeToJSDocType(input string, importNamespaces *slicer.StringSlicer) string { func fullyQualifiedName(packageName string, typeName string) string {
// Verifying this first to ensure we are not converting a type if len(packageName) > 0 {
// coming from a package that has a name matching a golang type, such as: return packageName + "." + typeName
// - interactor -> int
// - mapper -> map
if strings.ContainsRune(input, '.') {
namespace := getPackageName(input)
importNamespaces.Add(namespace)
return namespace + "." + strings.Split(input, ".")[1]
} }
switch true { switch true {
case input == "interface {}" || input == "interface{}": case len(typeName) == 0:
return ""
case typeName == "interface{}" || typeName == "interface {}":
return "any" return "any"
case input == "string": case typeName == "string":
return "string" return "string"
case input == "error": case typeName == "error":
return "Error" return "Error"
case case
strings.HasPrefix(input, "int"), strings.HasPrefix(typeName, "int"),
strings.HasPrefix(input, "uint"), strings.HasPrefix(typeName, "uint"),
strings.HasPrefix(input, "float"): strings.HasPrefix(typeName, "float"):
return "number" return "number"
case input == "bool": case typeName == "bool":
return "boolean" 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: default:
return "any" return "any"
} }
} }
func goTypeToTypescriptType(input string, importNamespaces *slicer.StringSlicer) string { func arrayifyValue(valueArray string, valueType string) string {
if strings.HasPrefix(input, "[]") { if len(valueArray) == 0 {
arrayType := goTypeToJSDocType(input[2:], importNamespaces) return valueType
return "Array<" + arrayType + ">"
} }
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) return goTypeToJSDocType(input, importNamespaces)
} }