Switch remote deploy to vendored source builds
Move remote deployment to a vendored source bundle built on the target host via Docker so redeploys no longer require local cross-compilation or host Go installation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
384
vendor/github.com/wailsapp/wails/v2/internal/binding/binding.go
generated
vendored
Normal file
384
vendor/github.com/wailsapp/wails/v2/internal/binding/binding.go
generated
vendored
Normal file
@@ -0,0 +1,384 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/typescriptify"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
)
|
||||
|
||||
type Bindings struct {
|
||||
db *DB
|
||||
logger logger.CustomLogger
|
||||
exemptions slicer.StringSlicer
|
||||
|
||||
structsToGenerateTS map[string]map[string]interface{}
|
||||
enumsToGenerateTS map[string]map[string]interface{}
|
||||
tsPrefix string
|
||||
tsSuffix string
|
||||
tsInterface bool
|
||||
obfuscate bool
|
||||
}
|
||||
|
||||
// NewBindings returns a new Bindings object
|
||||
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}, obfuscate bool, enumsToBind []interface{}) *Bindings {
|
||||
result := &Bindings{
|
||||
db: newDB(),
|
||||
logger: logger.CustomLogger("Bindings"),
|
||||
structsToGenerateTS: make(map[string]map[string]interface{}),
|
||||
enumsToGenerateTS: make(map[string]map[string]interface{}),
|
||||
obfuscate: obfuscate,
|
||||
}
|
||||
|
||||
for _, exemption := range exemptions {
|
||||
if exemption == nil {
|
||||
continue
|
||||
}
|
||||
name := runtime.FuncForPC(reflect.ValueOf(exemption).Pointer()).Name()
|
||||
// Yuk yuk yuk! Is there a better way?
|
||||
name = strings.TrimSuffix(name, "-fm")
|
||||
result.exemptions.Add(name)
|
||||
}
|
||||
|
||||
for _, enum := range enumsToBind {
|
||||
result.AddEnumToGenerateTS(enum)
|
||||
}
|
||||
|
||||
// Add the structs to bind
|
||||
for _, ptr := range structPointersToBind {
|
||||
err := result.Add(ptr)
|
||||
if err != nil {
|
||||
logger.Fatal("Error during binding: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Add the given struct methods to the Bindings
|
||||
func (b *Bindings) Add(structPtr interface{}) error {
|
||||
methods, err := b.getMethods(structPtr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot bind value to app: %s", err.Error())
|
||||
}
|
||||
|
||||
for _, method := range methods {
|
||||
b.db.AddMethod(method.Path.Package, method.Path.Struct, method.Path.Name, method)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bindings) DB() *DB {
|
||||
return b.db
|
||||
}
|
||||
|
||||
func (b *Bindings) ToJSON() (string, error) {
|
||||
return b.db.ToJSON()
|
||||
}
|
||||
|
||||
func (b *Bindings) GenerateModels() ([]byte, error) {
|
||||
models := map[string]string{}
|
||||
var seen slicer.StringSlicer
|
||||
var seenEnumsPackages slicer.StringSlicer
|
||||
allStructNames := b.getAllStructNames()
|
||||
allStructNames.Sort()
|
||||
allEnumNames := b.getAllEnumNames()
|
||||
allEnumNames.Sort()
|
||||
for packageName, structsToGenerate := range b.structsToGenerateTS {
|
||||
thisPackageCode := ""
|
||||
w := typescriptify.New()
|
||||
w.WithPrefix(b.tsPrefix)
|
||||
w.WithSuffix(b.tsSuffix)
|
||||
w.WithInterface(b.tsInterface)
|
||||
w.Namespace = packageName
|
||||
w.WithBackupDir("")
|
||||
w.KnownStructs = allStructNames
|
||||
w.KnownEnums = allEnumNames
|
||||
// sort the structs
|
||||
var structNames []string
|
||||
for structName := range structsToGenerate {
|
||||
structNames = append(structNames, structName)
|
||||
}
|
||||
sort.Strings(structNames)
|
||||
for _, structName := range structNames {
|
||||
fqstructname := packageName + "." + structName
|
||||
if seen.Contains(fqstructname) {
|
||||
continue
|
||||
}
|
||||
structInterface := structsToGenerate[structName]
|
||||
w.Add(structInterface)
|
||||
}
|
||||
|
||||
// if we have enums for this package, add them as well
|
||||
var enums, enumsExist = b.enumsToGenerateTS[packageName]
|
||||
if enumsExist {
|
||||
// Sort the enum names first to make the output deterministic
|
||||
sortedEnumNames := make([]string, 0, len(enums))
|
||||
for enumName := range enums {
|
||||
sortedEnumNames = append(sortedEnumNames, enumName)
|
||||
}
|
||||
sort.Strings(sortedEnumNames)
|
||||
|
||||
for _, enumName := range sortedEnumNames {
|
||||
enum := enums[enumName]
|
||||
fqemumname := packageName + "." + enumName
|
||||
if seen.Contains(fqemumname) {
|
||||
continue
|
||||
}
|
||||
w.AddEnum(enum)
|
||||
}
|
||||
seenEnumsPackages.Add(packageName)
|
||||
}
|
||||
|
||||
str, err := w.Convert(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
thisPackageCode += str
|
||||
seen.AddSlice(w.GetGeneratedStructs())
|
||||
models[packageName] = thisPackageCode
|
||||
}
|
||||
|
||||
// Add outstanding enums to the models that were not in packages with structs
|
||||
for packageName, enumsToGenerate := range b.enumsToGenerateTS {
|
||||
if seenEnumsPackages.Contains(packageName) {
|
||||
continue
|
||||
}
|
||||
|
||||
thisPackageCode := ""
|
||||
w := typescriptify.New()
|
||||
w.WithPrefix(b.tsPrefix)
|
||||
w.WithSuffix(b.tsSuffix)
|
||||
w.WithInterface(b.tsInterface)
|
||||
w.Namespace = packageName
|
||||
w.WithBackupDir("")
|
||||
|
||||
for enumName, enum := range enumsToGenerate {
|
||||
fqemumname := packageName + "." + enumName
|
||||
if seen.Contains(fqemumname) {
|
||||
continue
|
||||
}
|
||||
w.AddEnum(enum)
|
||||
}
|
||||
str, err := w.Convert(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
thisPackageCode += str
|
||||
models[packageName] = thisPackageCode
|
||||
}
|
||||
|
||||
// Sort the package names first to make the output deterministic
|
||||
sortedPackageNames := make([]string, 0, len(models))
|
||||
for packageName := range models {
|
||||
sortedPackageNames = append(sortedPackageNames, packageName)
|
||||
}
|
||||
sort.Strings(sortedPackageNames)
|
||||
|
||||
var modelsData bytes.Buffer
|
||||
for _, packageName := range sortedPackageNames {
|
||||
modelData := models[packageName]
|
||||
if strings.TrimSpace(modelData) == "" {
|
||||
continue
|
||||
}
|
||||
modelsData.WriteString("export namespace " + packageName + " {\n")
|
||||
sc := bufio.NewScanner(strings.NewReader(modelData))
|
||||
for sc.Scan() {
|
||||
modelsData.WriteString("\t" + sc.Text() + "\n")
|
||||
}
|
||||
modelsData.WriteString("\n}\n\n")
|
||||
}
|
||||
return modelsData.Bytes(), nil
|
||||
}
|
||||
|
||||
func (b *Bindings) WriteModels(modelsDir string) error {
|
||||
modelsData, err := b.GenerateModels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Don't write if we don't have anything
|
||||
if len(modelsData) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
filename := filepath.Join(modelsDir, "models.ts")
|
||||
err = os.WriteFile(filename, modelsData, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bindings) AddEnumToGenerateTS(e interface{}) {
|
||||
enumType := reflect.TypeOf(e)
|
||||
|
||||
var packageName string
|
||||
var enumName string
|
||||
// enums should be represented as array of all possible values
|
||||
if hasElements(enumType) {
|
||||
enum := enumType.Elem()
|
||||
// simple enum represented by struct with Value/TSName fields
|
||||
if enum.Kind() == reflect.Struct {
|
||||
_, tsNamePresented := enum.FieldByName("TSName")
|
||||
enumT, valuePresented := enum.FieldByName("Value")
|
||||
if tsNamePresented && valuePresented {
|
||||
packageName = getPackageName(enumT.Type.String())
|
||||
enumName = enumT.Type.Name()
|
||||
} else {
|
||||
return
|
||||
}
|
||||
// otherwise expecting implementation with TSName() https://github.com/tkrajina/typescriptify-golang-structs#enums-with-tsname
|
||||
} else {
|
||||
packageName = getPackageName(enumType.Elem().String())
|
||||
enumName = enumType.Elem().Name()
|
||||
}
|
||||
if b.enumsToGenerateTS[packageName] == nil {
|
||||
b.enumsToGenerateTS[packageName] = make(map[string]interface{})
|
||||
}
|
||||
if b.enumsToGenerateTS[packageName][enumName] != nil {
|
||||
return
|
||||
}
|
||||
b.enumsToGenerateTS[packageName][enumName] = e
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bindings) AddStructToGenerateTS(packageName string, structName string, s interface{}) {
|
||||
if b.structsToGenerateTS[packageName] == nil {
|
||||
b.structsToGenerateTS[packageName] = make(map[string]interface{})
|
||||
}
|
||||
if b.structsToGenerateTS[packageName][structName] != nil {
|
||||
return
|
||||
}
|
||||
b.structsToGenerateTS[packageName][structName] = s
|
||||
|
||||
// Iterate this struct and add any struct field references
|
||||
structType := reflect.TypeOf(s)
|
||||
for hasElements(structType) {
|
||||
structType = structType.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
field := structType.Field(i)
|
||||
if field.Anonymous || !field.IsExported() {
|
||||
continue
|
||||
}
|
||||
kind := field.Type.Kind()
|
||||
if kind == reflect.Struct {
|
||||
fqname := field.Type.String()
|
||||
sNameSplit := strings.SplitN(fqname, ".", 2)
|
||||
if len(sNameSplit) < 2 {
|
||||
continue
|
||||
}
|
||||
sName := sNameSplit[1]
|
||||
pName := getPackageName(fqname)
|
||||
a := reflect.New(field.Type)
|
||||
if b.hasExportedJSONFields(field.Type) {
|
||||
s := reflect.Indirect(a).Interface()
|
||||
b.AddStructToGenerateTS(pName, sName, s)
|
||||
}
|
||||
} else {
|
||||
fType := field.Type
|
||||
for hasElements(fType) {
|
||||
fType = fType.Elem()
|
||||
}
|
||||
if fType.Kind() == reflect.Struct {
|
||||
fqname := fType.String()
|
||||
sNameSplit := strings.SplitN(fqname, ".", 2)
|
||||
if len(sNameSplit) < 2 {
|
||||
continue
|
||||
}
|
||||
sName := sNameSplit[1]
|
||||
pName := getPackageName(fqname)
|
||||
a := reflect.New(fType)
|
||||
if b.hasExportedJSONFields(fType) {
|
||||
s := reflect.Indirect(a).Interface()
|
||||
b.AddStructToGenerateTS(pName, sName, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bindings) SetTsPrefix(prefix string) *Bindings {
|
||||
b.tsPrefix = prefix
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Bindings) SetTsSuffix(postfix string) *Bindings {
|
||||
b.tsSuffix = postfix
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Bindings) SetOutputType(outputType string) *Bindings {
|
||||
if outputType == "interfaces" {
|
||||
b.tsInterface = true
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Bindings) getAllStructNames() *slicer.StringSlicer {
|
||||
var result slicer.StringSlicer
|
||||
for packageName, structsToGenerate := range b.structsToGenerateTS {
|
||||
for structName := range structsToGenerate {
|
||||
result.Add(packageName + "." + structName)
|
||||
}
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
||||
func (b *Bindings) getAllEnumNames() *slicer.StringSlicer {
|
||||
var result slicer.StringSlicer
|
||||
for packageName, enumsToGenerate := range b.enumsToGenerateTS {
|
||||
for enumName := range enumsToGenerate {
|
||||
result.Add(packageName + "." + enumName)
|
||||
}
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
||||
func (b *Bindings) hasExportedJSONFields(typeOf reflect.Type) bool {
|
||||
for i := 0; i < typeOf.NumField(); i++ {
|
||||
jsonFieldName := ""
|
||||
f := typeOf.Field(i)
|
||||
// function, complex, and channel types cannot be json-encoded
|
||||
if f.Type.Kind() == reflect.Chan ||
|
||||
f.Type.Kind() == reflect.Func ||
|
||||
f.Type.Kind() == reflect.UnsafePointer ||
|
||||
f.Type.Kind() == reflect.Complex128 ||
|
||||
f.Type.Kind() == reflect.Complex64 {
|
||||
continue
|
||||
}
|
||||
jsonTag, hasTag := f.Tag.Lookup("json")
|
||||
if !hasTag && f.IsExported() {
|
||||
return true
|
||||
}
|
||||
if len(jsonTag) == 0 {
|
||||
continue
|
||||
}
|
||||
jsonTagParts := strings.Split(jsonTag, ",")
|
||||
if len(jsonTagParts) > 0 {
|
||||
jsonFieldName = jsonTagParts[0]
|
||||
}
|
||||
for _, t := range jsonTagParts {
|
||||
if t == "-" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if jsonFieldName != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
109
vendor/github.com/wailsapp/wails/v2/internal/binding/boundMethod.go
generated
vendored
Normal file
109
vendor/github.com/wailsapp/wails/v2/internal/binding/boundMethod.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type BoundedMethodPath struct {
|
||||
Package string
|
||||
Struct string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (p *BoundedMethodPath) FullName() string {
|
||||
return fmt.Sprintf("%s.%s.%s", p.Package, p.Struct, p.Name)
|
||||
}
|
||||
|
||||
// BoundMethod defines all the data related to a Go method that is
|
||||
// bound to the Wails application
|
||||
type BoundMethod struct {
|
||||
Path *BoundedMethodPath `json:"path"`
|
||||
Inputs []*Parameter `json:"inputs,omitempty"`
|
||||
Outputs []*Parameter `json:"outputs,omitempty"`
|
||||
Comments string `json:"comments,omitempty"`
|
||||
Method reflect.Value `json:"-"`
|
||||
}
|
||||
|
||||
// InputCount returns the number of inputs this bound method has
|
||||
func (b *BoundMethod) InputCount() int {
|
||||
return len(b.Inputs)
|
||||
}
|
||||
|
||||
// OutputCount returns the number of outputs this bound method has
|
||||
func (b *BoundMethod) OutputCount() int {
|
||||
return len(b.Outputs)
|
||||
}
|
||||
|
||||
// ParseArgs method converts the input json into the types expected by the method
|
||||
func (b *BoundMethod) ParseArgs(args []json.RawMessage) ([]interface{}, error) {
|
||||
result := make([]interface{}, b.InputCount())
|
||||
if len(args) != b.InputCount() {
|
||||
return nil, fmt.Errorf("received %d arguments to method '%s', expected %d", len(args), b.Path.FullName(), b.InputCount())
|
||||
}
|
||||
for index, arg := range args {
|
||||
typ := b.Inputs[index].reflectType
|
||||
inputValue := reflect.New(typ).Interface()
|
||||
err := json.Unmarshal(arg, inputValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if inputValue == nil {
|
||||
result[index] = reflect.Zero(typ).Interface()
|
||||
} else {
|
||||
result[index] = reflect.ValueOf(inputValue).Elem().Interface()
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Call will attempt to call this bound method with the given args
|
||||
func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
|
||||
// Check inputs
|
||||
expectedInputLength := len(b.Inputs)
|
||||
actualInputLength := len(args)
|
||||
if expectedInputLength != actualInputLength {
|
||||
return nil, fmt.Errorf("%s takes %d inputs. Received %d", b.Path.FullName(), expectedInputLength, actualInputLength)
|
||||
}
|
||||
|
||||
/** Convert inputs to reflect values **/
|
||||
|
||||
// Create slice for the input arguments to the method call
|
||||
callArgs := make([]reflect.Value, expectedInputLength)
|
||||
|
||||
// Iterate over given arguments
|
||||
for index, arg := range args {
|
||||
// Save the converted argument
|
||||
callArgs[index] = reflect.ValueOf(arg)
|
||||
}
|
||||
|
||||
// Do the call
|
||||
callResults := b.Method.Call(callArgs)
|
||||
|
||||
//** Check results **//
|
||||
var returnValue interface{}
|
||||
var err error
|
||||
|
||||
switch b.OutputCount() {
|
||||
case 1:
|
||||
// Loop over results and determine if the result
|
||||
// is an error or not
|
||||
for _, result := range callResults {
|
||||
interfac := result.Interface()
|
||||
temp, ok := interfac.(error)
|
||||
if ok {
|
||||
err = temp
|
||||
} else {
|
||||
returnValue = interfac
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
returnValue = callResults[0].Interface()
|
||||
if temp, ok := callResults[1].Interface().(error); ok {
|
||||
err = temp
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue, err
|
||||
}
|
||||
134
vendor/github.com/wailsapp/wails/v2/internal/binding/db.go
generated
vendored
Normal file
134
vendor/github.com/wailsapp/wails/v2/internal/binding/db.go
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// DB is our database of method bindings
|
||||
type DB struct {
|
||||
// map[packagename] -> map[structname] -> map[methodname]*method
|
||||
store map[string]map[string]map[string]*BoundMethod
|
||||
|
||||
// This uses fully qualified method names as a shortcut for store traversal.
|
||||
// It used for performance gains at runtime
|
||||
methodMap map[string]*BoundMethod
|
||||
|
||||
// This uses ids to reference bound methods at runtime
|
||||
obfuscatedMethodArray []*ObfuscatedMethod
|
||||
|
||||
// Lock to ensure sync access to the data
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
type ObfuscatedMethod struct {
|
||||
method *BoundMethod
|
||||
methodName string
|
||||
}
|
||||
|
||||
func newDB() *DB {
|
||||
return &DB{
|
||||
store: make(map[string]map[string]map[string]*BoundMethod),
|
||||
methodMap: make(map[string]*BoundMethod),
|
||||
obfuscatedMethodArray: []*ObfuscatedMethod{},
|
||||
}
|
||||
}
|
||||
|
||||
// GetMethodFromStore returns the method for the given package/struct/method names
|
||||
// nil is returned if any one of those does not exist
|
||||
func (d *DB) GetMethodFromStore(packageName string, structName string, methodName string) *BoundMethod {
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.RLock()
|
||||
defer d.lock.RUnlock()
|
||||
|
||||
structMap, exists := d.store[packageName]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
methodMap, exists := structMap[structName]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
return methodMap[methodName]
|
||||
}
|
||||
|
||||
// GetMethod returns the method for the given qualified method name
|
||||
// qualifiedMethodName is "packagename.structname.methodname"
|
||||
func (d *DB) GetMethod(qualifiedMethodName string) *BoundMethod {
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.RLock()
|
||||
defer d.lock.RUnlock()
|
||||
|
||||
return d.methodMap[qualifiedMethodName]
|
||||
}
|
||||
|
||||
// GetObfuscatedMethod returns the method for the given ID
|
||||
func (d *DB) GetObfuscatedMethod(id int) *BoundMethod {
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.RLock()
|
||||
defer d.lock.RUnlock()
|
||||
|
||||
if len(d.obfuscatedMethodArray) <= id {
|
||||
return nil
|
||||
}
|
||||
|
||||
return d.obfuscatedMethodArray[id].method
|
||||
}
|
||||
|
||||
// AddMethod adds the given method definition to the db using the given qualified path: packageName.structName.methodName
|
||||
func (d *DB) AddMethod(packageName string, structName string, methodName string, methodDefinition *BoundMethod) {
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
// Get the map associated with the package name
|
||||
structMap, exists := d.store[packageName]
|
||||
if !exists {
|
||||
// Create a new map for this packagename
|
||||
d.store[packageName] = make(map[string]map[string]*BoundMethod)
|
||||
structMap = d.store[packageName]
|
||||
}
|
||||
|
||||
// Get the map associated with the struct name
|
||||
methodMap, exists := structMap[structName]
|
||||
if !exists {
|
||||
// Create a new map for this packagename
|
||||
structMap[structName] = make(map[string]*BoundMethod)
|
||||
methodMap = structMap[structName]
|
||||
}
|
||||
|
||||
// Store the method definition
|
||||
methodMap[methodName] = methodDefinition
|
||||
|
||||
// Store in the methodMap
|
||||
key := packageName + "." + structName + "." + methodName
|
||||
d.methodMap[key] = methodDefinition
|
||||
d.obfuscatedMethodArray = append(d.obfuscatedMethodArray, &ObfuscatedMethod{method: methodDefinition, methodName: key})
|
||||
}
|
||||
|
||||
// ToJSON converts the method map to JSON
|
||||
func (d *DB) ToJSON() (string, error) {
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.RLock()
|
||||
defer d.lock.RUnlock()
|
||||
|
||||
d.UpdateObfuscatedCallMap()
|
||||
|
||||
bytes, err := json.Marshal(&d.store)
|
||||
|
||||
// Return zero copy string as this string will be read only
|
||||
result := *(*string)(unsafe.Pointer(&bytes))
|
||||
return result, err
|
||||
}
|
||||
|
||||
// UpdateObfuscatedCallMap sets up the secure call mappings
|
||||
func (d *DB) UpdateObfuscatedCallMap() map[string]int {
|
||||
mappings := make(map[string]int)
|
||||
|
||||
for id, k := range d.obfuscatedMethodArray {
|
||||
mappings[k.methodName] = id
|
||||
}
|
||||
|
||||
return mappings
|
||||
}
|
||||
248
vendor/github.com/wailsapp/wails/v2/internal/binding/generate.go
generated
vendored
Normal file
248
vendor/github.com/wailsapp/wails/v2/internal/binding/generate.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
)
|
||||
|
||||
var (
|
||||
mapRegex *regexp.Regexp
|
||||
keyPackageIndex int
|
||||
keyTypeIndex int
|
||||
valueArrayIndex int
|
||||
valuePackageIndex int
|
||||
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
|
||||
if b.obfuscate {
|
||||
obfuscatedBindings = b.db.UpdateObfuscatedCallMap()
|
||||
}
|
||||
for packageName, structs := range store {
|
||||
packageDir := filepath.Join(baseDir, packageName)
|
||||
err := fs.Mkdir(packageDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for structName, methods := range structs {
|
||||
var jsoutput bytes.Buffer
|
||||
jsoutput.WriteString(`// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
`)
|
||||
var tsBody bytes.Buffer
|
||||
var tsContent bytes.Buffer
|
||||
tsContent.WriteString(`// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
`)
|
||||
// Sort the method names alphabetically
|
||||
methodNames := make([]string, 0, len(methods))
|
||||
for methodName := range methods {
|
||||
methodNames = append(methodNames, methodName)
|
||||
}
|
||||
sort.Strings(methodNames)
|
||||
|
||||
var importNamespaces slicer.StringSlicer
|
||||
for _, methodName := range methodNames {
|
||||
// Get the method details
|
||||
methodDetails := methods[methodName]
|
||||
|
||||
// Generate JS
|
||||
var args slicer.StringSlicer
|
||||
for count := range methodDetails.Inputs {
|
||||
arg := fmt.Sprintf("arg%d", count+1)
|
||||
args.Add(arg)
|
||||
}
|
||||
argsString := args.Join(", ")
|
||||
jsoutput.WriteString(fmt.Sprintf("\nexport function %s(%s) {", methodName, argsString))
|
||||
jsoutput.WriteString("\n")
|
||||
if b.obfuscate {
|
||||
id := obfuscatedBindings[strings.Join([]string{packageName, structName, methodName}, ".")]
|
||||
jsoutput.WriteString(fmt.Sprintf(" return ObfuscatedCall(%d, [%s]);", id, argsString))
|
||||
} else {
|
||||
jsoutput.WriteString(fmt.Sprintf(" return window['go']['%s']['%s']['%s'](%s);", packageName, structName, methodName, argsString))
|
||||
}
|
||||
jsoutput.WriteString("\n}\n")
|
||||
|
||||
// Generate TS
|
||||
tsBody.WriteString(fmt.Sprintf("\nexport function %s(", methodName))
|
||||
|
||||
args.Clear()
|
||||
for count, input := range methodDetails.Inputs {
|
||||
arg := fmt.Sprintf("arg%d", count+1)
|
||||
entityName := entityFullReturnType(input.TypeName, b.tsPrefix, b.tsSuffix, &importNamespaces)
|
||||
args.Add(arg + ":" + goTypeToTypescriptType(entityName, &importNamespaces))
|
||||
}
|
||||
tsBody.WriteString(args.Join(",") + "):")
|
||||
// now build Typescript return types
|
||||
// If there is no return value or only returning error, TS returns Promise<void>
|
||||
// If returning single value, TS returns Promise<type>
|
||||
// If returning single value or error, TS returns Promise<type>
|
||||
// If returning two values, TS returns Promise<type1|type2>
|
||||
// Otherwise, TS returns Promise<type1> (instead of throwing Go error?)
|
||||
var returnType string
|
||||
if methodDetails.OutputCount() == 0 {
|
||||
returnType = "Promise<void>"
|
||||
} else if methodDetails.OutputCount() == 1 && methodDetails.Outputs[0].TypeName == "error" {
|
||||
returnType = "Promise<void>"
|
||||
} else {
|
||||
outputTypeName := entityFullReturnType(methodDetails.Outputs[0].TypeName, b.tsPrefix, b.tsSuffix, &importNamespaces)
|
||||
firstType := goTypeToTypescriptType(outputTypeName, &importNamespaces)
|
||||
returnType = "Promise<" + firstType
|
||||
if methodDetails.OutputCount() == 2 && methodDetails.Outputs[1].TypeName != "error" {
|
||||
outputTypeName = entityFullReturnType(methodDetails.Outputs[1].TypeName, b.tsPrefix, b.tsSuffix, &importNamespaces)
|
||||
secondType := goTypeToTypescriptType(outputTypeName, &importNamespaces)
|
||||
returnType += "|" + secondType
|
||||
}
|
||||
returnType += ">"
|
||||
}
|
||||
tsBody.WriteString(returnType + ";\n")
|
||||
}
|
||||
|
||||
importNamespaces.Deduplicate()
|
||||
importNamespaces.Each(func(namespace string) {
|
||||
tsContent.WriteString("import {" + namespace + "} from '../models';\n")
|
||||
})
|
||||
tsContent.WriteString(tsBody.String())
|
||||
|
||||
jsfilename := filepath.Join(packageDir, structName+".js")
|
||||
err = os.WriteFile(jsfilename, jsoutput.Bytes(), 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tsfilename := filepath.Join(packageDir, structName+".d.ts")
|
||||
err = os.WriteFile(tsfilename, tsContent.Bytes(), 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
err := b.WriteModels(baseDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fullyQualifiedName(packageName string, typeName string) string {
|
||||
if len(packageName) > 0 {
|
||||
return packageName + "." + typeName
|
||||
}
|
||||
|
||||
switch true {
|
||||
case len(typeName) == 0:
|
||||
return ""
|
||||
case typeName == "interface{}" || typeName == "interface {}":
|
||||
return "any"
|
||||
case typeName == "string":
|
||||
return "string"
|
||||
case typeName == "error":
|
||||
return "Error"
|
||||
case
|
||||
strings.HasPrefix(typeName, "int"),
|
||||
strings.HasPrefix(typeName, "uint"),
|
||||
strings.HasPrefix(typeName, "float"):
|
||||
return "number"
|
||||
case typeName == "bool":
|
||||
return "boolean"
|
||||
default:
|
||||
return "any"
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
jsVariableUnsafeChars = regexp.MustCompile(`[^A-Za-z0-9_]`)
|
||||
)
|
||||
|
||||
func arrayifyValue(valueArray string, valueType string) string {
|
||||
valueType = strings.ReplaceAll(valueType, "*", "")
|
||||
gidx := strings.IndexRune(valueType, '[')
|
||||
if gidx > 0 { // its a generic type
|
||||
rem := strings.SplitN(valueType, "[", 2)
|
||||
valueType = rem[0] + "_" + jsVariableUnsafeChars.ReplaceAllLiteralString(rem[1], "_")
|
||||
}
|
||||
|
||||
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("Record<%s, %s>", key, arrayifyValue(valueArray, value))
|
||||
}
|
||||
|
||||
return arrayifyValue(valueArray, value)
|
||||
}
|
||||
|
||||
func goTypeToTypescriptType(input string, importNamespaces *slicer.StringSlicer) string {
|
||||
return goTypeToJSDocType(input, importNamespaces)
|
||||
}
|
||||
|
||||
func entityFullReturnType(input, prefix, suffix string, importNamespaces *slicer.StringSlicer) string {
|
||||
if strings.ContainsRune(input, '.') {
|
||||
nameSpace, returnType := getSplitReturn(input)
|
||||
return nameSpace + "." + prefix + returnType + suffix
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
28
vendor/github.com/wailsapp/wails/v2/internal/binding/parameter.go
generated
vendored
Normal file
28
vendor/github.com/wailsapp/wails/v2/internal/binding/parameter.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package binding
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Parameter defines a Go method parameter
|
||||
type Parameter struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
TypeName string `json:"type"`
|
||||
reflectType reflect.Type
|
||||
}
|
||||
|
||||
func newParameter(Name string, Type reflect.Type) *Parameter {
|
||||
return &Parameter{
|
||||
Name: Name,
|
||||
TypeName: Type.String(),
|
||||
reflectType: Type,
|
||||
}
|
||||
}
|
||||
|
||||
// IsType returns true if the given
|
||||
func (p *Parameter) IsType(typename string) bool {
|
||||
return p.TypeName == typename
|
||||
}
|
||||
|
||||
// IsError returns true if the parameter type is an error
|
||||
func (p *Parameter) IsError() bool {
|
||||
return p.IsType("error")
|
||||
}
|
||||
200
vendor/github.com/wailsapp/wails/v2/internal/binding/reflect.go
generated
vendored
Normal file
200
vendor/github.com/wailsapp/wails/v2/internal/binding/reflect.go
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// isStructPtr returns true if the value given is a
|
||||
// pointer to a struct
|
||||
func isStructPtr(value interface{}) bool {
|
||||
return reflect.ValueOf(value).Kind() == reflect.Ptr &&
|
||||
reflect.ValueOf(value).Elem().Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
// isFunction returns true if the given value is a function
|
||||
func isFunction(value interface{}) bool {
|
||||
return reflect.ValueOf(value).Kind() == reflect.Func
|
||||
}
|
||||
|
||||
// isStruct returns true if the value given is a struct
|
||||
func isStruct(value interface{}) bool {
|
||||
return reflect.ValueOf(value).Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
func normalizeStructName(name string) string {
|
||||
return strings.ReplaceAll(
|
||||
strings.ReplaceAll(
|
||||
strings.ReplaceAll(
|
||||
strings.ReplaceAll(
|
||||
name,
|
||||
",",
|
||||
"-",
|
||||
),
|
||||
"*",
|
||||
"",
|
||||
),
|
||||
"]",
|
||||
"__",
|
||||
),
|
||||
"[",
|
||||
"__",
|
||||
)
|
||||
}
|
||||
|
||||
func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
||||
// Create result placeholder
|
||||
var result []*BoundMethod
|
||||
|
||||
// Check type
|
||||
if !isStructPtr(value) {
|
||||
|
||||
if isStruct(value) {
|
||||
name := reflect.ValueOf(value).Type().Name()
|
||||
return nil, fmt.Errorf("%s is a struct, not a pointer to a struct", name)
|
||||
}
|
||||
|
||||
if isFunction(value) {
|
||||
name := runtime.FuncForPC(reflect.ValueOf(value).Pointer()).Name()
|
||||
return nil, fmt.Errorf("%s is a function, not a pointer to a struct. Wails v2 has deprecated the binding of functions. Please wrap your functions up in a struct and bind a pointer to that struct.", name)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("not a pointer to a struct.")
|
||||
}
|
||||
|
||||
// Process Struct
|
||||
structType := reflect.TypeOf(value)
|
||||
structValue := reflect.ValueOf(value)
|
||||
structName := structType.Elem().Name()
|
||||
structNameNormalized := normalizeStructName(structName)
|
||||
pkgPath := strings.TrimSuffix(structType.Elem().String(), fmt.Sprintf(".%s", structName))
|
||||
|
||||
// Process Methods
|
||||
for i := 0; i < structType.NumMethod(); i++ {
|
||||
methodDef := structType.Method(i)
|
||||
methodName := methodDef.Name
|
||||
method := structValue.MethodByName(methodName)
|
||||
|
||||
methodReflectName := runtime.FuncForPC(methodDef.Func.Pointer()).Name()
|
||||
if b.exemptions.Contains(methodReflectName) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Create new method
|
||||
boundMethod := &BoundMethod{
|
||||
Path: &BoundedMethodPath{
|
||||
Package: pkgPath,
|
||||
Struct: structNameNormalized,
|
||||
Name: methodName,
|
||||
},
|
||||
Inputs: nil,
|
||||
Outputs: nil,
|
||||
Comments: "",
|
||||
Method: method,
|
||||
}
|
||||
|
||||
// Iterate inputs
|
||||
methodType := method.Type()
|
||||
inputParamCount := methodType.NumIn()
|
||||
var inputs []*Parameter
|
||||
for inputIndex := 0; inputIndex < inputParamCount; inputIndex++ {
|
||||
input := methodType.In(inputIndex)
|
||||
thisParam := newParameter("", input)
|
||||
|
||||
thisInput := input
|
||||
|
||||
if thisInput.Kind() == reflect.Slice {
|
||||
thisInput = thisInput.Elem()
|
||||
}
|
||||
|
||||
// Process struct pointer params
|
||||
if thisInput.Kind() == reflect.Ptr {
|
||||
if thisInput.Elem().Kind() == reflect.Struct {
|
||||
typ := thisInput.Elem()
|
||||
a := reflect.New(typ)
|
||||
s := reflect.Indirect(a).Interface()
|
||||
name := typ.Name()
|
||||
packageName := getPackageName(thisInput.String())
|
||||
b.AddStructToGenerateTS(packageName, name, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Process struct params
|
||||
if thisInput.Kind() == reflect.Struct {
|
||||
a := reflect.New(thisInput)
|
||||
s := reflect.Indirect(a).Interface()
|
||||
name := thisInput.Name()
|
||||
packageName := getPackageName(thisInput.String())
|
||||
b.AddStructToGenerateTS(packageName, name, s)
|
||||
}
|
||||
|
||||
inputs = append(inputs, thisParam)
|
||||
}
|
||||
|
||||
boundMethod.Inputs = inputs
|
||||
|
||||
// Iterate outputs
|
||||
// TODO: Determine what to do about limiting return types
|
||||
// especially around errors.
|
||||
outputParamCount := methodType.NumOut()
|
||||
var outputs []*Parameter
|
||||
for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ {
|
||||
output := methodType.Out(outputIndex)
|
||||
thisParam := newParameter("", output)
|
||||
|
||||
thisOutput := output
|
||||
|
||||
if thisOutput.Kind() == reflect.Slice {
|
||||
thisOutput = thisOutput.Elem()
|
||||
}
|
||||
|
||||
// Process struct pointer params
|
||||
if thisOutput.Kind() == reflect.Ptr {
|
||||
if thisOutput.Elem().Kind() == reflect.Struct {
|
||||
typ := thisOutput.Elem()
|
||||
a := reflect.New(typ)
|
||||
s := reflect.Indirect(a).Interface()
|
||||
name := typ.Name()
|
||||
packageName := getPackageName(thisOutput.String())
|
||||
b.AddStructToGenerateTS(packageName, name, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Process struct params
|
||||
if thisOutput.Kind() == reflect.Struct {
|
||||
a := reflect.New(thisOutput)
|
||||
s := reflect.Indirect(a).Interface()
|
||||
name := thisOutput.Name()
|
||||
packageName := getPackageName(thisOutput.String())
|
||||
b.AddStructToGenerateTS(packageName, name, s)
|
||||
}
|
||||
|
||||
outputs = append(outputs, thisParam)
|
||||
}
|
||||
boundMethod.Outputs = outputs
|
||||
|
||||
// Save method in result
|
||||
result = append(result, boundMethod)
|
||||
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getPackageName(in string) string {
|
||||
result := strings.Split(in, ".")[0]
|
||||
result = strings.ReplaceAll(result, "[]", "")
|
||||
result = strings.ReplaceAll(result, "*", "")
|
||||
return result
|
||||
}
|
||||
|
||||
func getSplitReturn(in string) (string, string) {
|
||||
result := strings.SplitN(in, ".", 2)
|
||||
return result[0], result[1]
|
||||
}
|
||||
|
||||
func hasElements(typ reflect.Type) bool {
|
||||
kind := typ.Kind()
|
||||
return kind == reflect.Ptr || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map
|
||||
}
|
||||
Reference in New Issue
Block a user