mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-19 02:19:31 +08:00
Support bindings, model & enum generation
This commit is contained in:
parent
43c4966873
commit
4b04c10f14
@ -1,15 +1,10 @@
|
||||
package commands
|
||||
|
||||
import "github.com/wailsapp/wails/v3/internal/parser"
|
||||
import (
|
||||
"github.com/wailsapp/wails/v3/internal/flags"
|
||||
"github.com/wailsapp/wails/v3/internal/parser"
|
||||
)
|
||||
|
||||
type GenerateBindingsOptions struct {
|
||||
Silent bool `name:"silent" description:"Silent mode"`
|
||||
ModelsFilename string `name:"m" description:"The filename for the models file" default:"models.ts"`
|
||||
BindingsFilename string `name:"b" description:"The filename for the bindings file" default:"bindings_<package>.js"`
|
||||
ProjectDirectory string `name:"p" description:"The project directory" default:"."`
|
||||
OutputDirectory string `name:"d" description:"The output directory" default:"."`
|
||||
}
|
||||
|
||||
func GenerateBindings(options *GenerateBindingsOptions) error {
|
||||
return parser.GenerateBindingsAndModels(options.ProjectDirectory, options.OutputDirectory)
|
||||
func GenerateBindings(options *flags.GenerateBindingsOptions) error {
|
||||
return parser.GenerateBindingsAndModels(options)
|
||||
}
|
||||
|
11
v3/internal/flags/bindings.go
Normal file
11
v3/internal/flags/bindings.go
Normal file
@ -0,0 +1,11 @@
|
||||
package flags
|
||||
|
||||
type GenerateBindingsOptions struct {
|
||||
Silent bool `name:"silent" description:"Silent mode"`
|
||||
ModelsFilename string `name:"m" description:"The filename for the models file" default:"models.ts"`
|
||||
TSPrefix string `description:"The prefix for the typescript names" default:""`
|
||||
TSSuffix string `description:"The postfix for the typescript names" default:""`
|
||||
UseInterfaces bool `name:"i" description:"Use interfaces instead of classes"`
|
||||
ProjectDirectory string `name:"p" description:"The project directory" default:"."`
|
||||
OutputDirectory string `name:"d" description:"The output directory" default:"frontend/bindings"`
|
||||
}
|
@ -58,6 +58,9 @@ This package contains the static analyser used for parsing Wails projects so tha
|
||||
- [ ] Maps
|
||||
- [x] Structs
|
||||
- [ ] Generation of bindings
|
||||
- [ ] Classes
|
||||
- [ ] Interfaces
|
||||
- [ ] Enums
|
||||
|
||||
## Limitations
|
||||
|
||||
|
@ -19,12 +19,18 @@ const bindingTemplate = `
|
||||
/**
|
||||
* {{structName}}.{{methodName}}
|
||||
*Comments
|
||||
* @param name {string}
|
||||
* @param names {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
{{methodName}}: function({{inputs}}) { return wails.CallByID({{ID}}, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
`
|
||||
|
||||
const enumTemplate = `
|
||||
export enum {{.EnumName}} {
|
||||
{{.EnumValues}}
|
||||
}
|
||||
`
|
||||
|
||||
var reservedWords = []string{
|
||||
"abstract",
|
||||
"arguments",
|
||||
@ -119,18 +125,19 @@ func GenerateBinding(structName string, method *BoundMethod) (string, []string,
|
||||
pkgName := getPackageName(input)
|
||||
if pkgName != "" {
|
||||
models = append(models, pkgName)
|
||||
if input.Type.IsStruct {
|
||||
nsStruct := input.NamespacedStructType()
|
||||
namespacedStructs = append(namespacedStructs, nsStruct)
|
||||
}
|
||||
}
|
||||
params += " * @param " + inputName + " {" + input.JSType() + "}\n"
|
||||
if input.Type.IsStruct || input.Type.IsEnum {
|
||||
nsStruct := input.NamespacedStructType()
|
||||
namespacedStructs = append(namespacedStructs, nsStruct)
|
||||
}
|
||||
|
||||
params += " * @param " + inputName + " {" + input.JSType() + "}\n"
|
||||
}
|
||||
params = strings.TrimSuffix(params, "\n")
|
||||
if len(params) == 0 {
|
||||
params = " *"
|
||||
params = " *"
|
||||
}
|
||||
result = strings.ReplaceAll(result, " * @param name {string}", params)
|
||||
result = strings.ReplaceAll(result, "* @param names {string}", params)
|
||||
var inputs string
|
||||
for _, input := range method.Inputs {
|
||||
pkgName := getPackageName(input)
|
||||
@ -232,19 +239,8 @@ func GenerateBindings(bindings map[string]map[string][]*BoundMethod) map[string]
|
||||
packageBindings := bindings[packageName]
|
||||
structNames := lo.Keys(packageBindings)
|
||||
sort.Strings(structNames)
|
||||
result[normalisedPackageNames[packageName]] += `
|
||||
window.go = window.go || {};
|
||||
`
|
||||
// Iterate over the sorted struct keys
|
||||
result[normalisedPackageNames[packageName]] += "window.go." + normalisedPackageNames[packageName] + " = {\n"
|
||||
for _, structName := range structNames {
|
||||
/**
|
||||
* The GreetService provides methods to greet a person.
|
||||
*/
|
||||
//result[normalisedPackageNames[packageName]] += " /**\n"
|
||||
//result[normalisedPackageNames[packageName]] += " {{structcomments}}\n"
|
||||
//result[normalisedPackageNames[packageName]] += " */\n"
|
||||
result[normalisedPackageNames[packageName]] += " " + structName + ": {\n"
|
||||
result[normalisedPackageNames[packageName]] += "export const " + structName + " = {\n"
|
||||
methods := packageBindings[structName]
|
||||
sort.Slice(methods, func(i, j int) bool {
|
||||
return methods[i].Name < methods[j].Name
|
||||
@ -255,9 +251,8 @@ window.go = window.go || {};
|
||||
allModels = append(allModels, models...)
|
||||
result[normalisedPackageNames[packageName]] += thisBinding
|
||||
}
|
||||
result[normalisedPackageNames[packageName]] += " },\n"
|
||||
result[normalisedPackageNames[packageName]] += "};\n\n"
|
||||
}
|
||||
result[normalisedPackageNames[packageName]] += "};\n"
|
||||
|
||||
if len(allNamespacedStructs) > 0 {
|
||||
typedefs := "/**\n"
|
||||
|
@ -27,6 +27,12 @@ func TestGenerateBindings(t *testing.T) {
|
||||
dir string
|
||||
want map[string]string
|
||||
}{
|
||||
{
|
||||
"testdata/enum",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/enum/bindings_main.js"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"testdata/function_single",
|
||||
map[string]string{
|
||||
|
@ -3,6 +3,7 @@ package parser
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"github.com/wailsapp/wails/v3/internal/flags"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -15,16 +16,27 @@ var templates embed.FS
|
||||
type ModelDefinitions struct {
|
||||
Package string
|
||||
Models map[string]*StructDef
|
||||
Enums map[string]*TypeDef
|
||||
}
|
||||
|
||||
func GenerateModel(wr io.Writer, def *ModelDefinitions) error {
|
||||
tmpl, err := template.New("model.ts.tmpl").ParseFS(templates, "templates/model.ts.tmpl")
|
||||
func GenerateModel(wr io.Writer, def *ModelDefinitions, options *flags.GenerateBindingsOptions) error {
|
||||
templateName := "model.ts.tmpl"
|
||||
if options.UseInterfaces {
|
||||
templateName = "interfaces.ts.tmpl"
|
||||
}
|
||||
|
||||
// Fix up TS names
|
||||
for _, model := range def.Models {
|
||||
model.Name = options.TSPrefix + model.Name + options.TSSuffix
|
||||
}
|
||||
|
||||
tmpl, err := template.New(templateName).ParseFS(templates, "templates/"+templateName)
|
||||
if err != nil {
|
||||
println("Unable to create class template: " + err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
err = tmpl.ExecuteTemplate(wr, "model.ts.tmpl", def)
|
||||
err = tmpl.ExecuteTemplate(wr, templateName, def)
|
||||
if err != nil {
|
||||
println("Problem executing template: " + err.Error())
|
||||
return err
|
||||
@ -42,7 +54,7 @@ func pkgAlias(fullPkg string) string {
|
||||
return pkgParts[len(pkgParts)-1]
|
||||
}
|
||||
|
||||
func GenerateModels(models map[packagePath]map[structName]*StructDef) (string, error) {
|
||||
func GenerateModels(models map[packagePath]map[structName]*StructDef, enums map[packagePath]map[string]*TypeDef, options *flags.GenerateBindingsOptions) (string, error) {
|
||||
if models == nil {
|
||||
return "", nil
|
||||
}
|
||||
@ -53,7 +65,7 @@ func GenerateModels(models map[packagePath]map[structName]*StructDef) (string, e
|
||||
// sort pkgs by alias (e.g. services) instead of full pkg name (e.g. github.com/wailsapp/wails/somedir/services)
|
||||
// and then sort resulting list by the alias
|
||||
var keys []string
|
||||
for pkg, _ := range models {
|
||||
for pkg := range models {
|
||||
keys = append(keys, pkg)
|
||||
}
|
||||
|
||||
@ -65,120 +77,11 @@ func GenerateModels(models map[packagePath]map[structName]*StructDef) (string, e
|
||||
err := GenerateModel(&buffer, &ModelDefinitions{
|
||||
Package: pkgAlias(pkg),
|
||||
Models: models[pkg],
|
||||
})
|
||||
Enums: enums[pkg],
|
||||
}, options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
//func GenerateClass(wr io.Writer, def *StructDef) error {
|
||||
// tmpl, err := template.New("class.ts.tmpl").ParseFiles("templates/class.ts.tmpl")
|
||||
// if err != nil {
|
||||
// println("Unable to create class template: " + err.Error())
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// err = tmpl.ExecuteTemplate(wr, "class.ts.tmpl", def)
|
||||
// if err != nil {
|
||||
// println("Problem executing template: " + err.Error())
|
||||
// return err
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
|
||||
//
|
||||
//import (
|
||||
// "bytes"
|
||||
// "fmt"
|
||||
// "go/ast"
|
||||
// "go/types"
|
||||
// "sort"
|
||||
// "strings"
|
||||
// "unicode"
|
||||
//)
|
||||
//
|
||||
//func GenerateModels(context *Context) ([]byte, error) {
|
||||
// var buf bytes.Buffer
|
||||
// var pkgs []Package
|
||||
// specs := context.GetBoundStructs()
|
||||
// for pkg, pkgSpecs := range specs {
|
||||
// pkgs = append(pkgs, Package{Name: pkg, Specs: pkgSpecs})
|
||||
// }
|
||||
// knownStructs := newAllModels(specs)
|
||||
// sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].Name < pkgs[j].Name })
|
||||
// for _, pkg := range pkgs {
|
||||
// if _, err := fmt.Fprintf(&buf, "namespace %s {\n", pkg.Name); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// sort.Slice(pkg.Specs, func(i, j int) bool { return pkg.Specs[i].Name.Name < pkg.Specs[j].Name.Name })
|
||||
// for _, spec := range pkg.Specs {
|
||||
// if structType, ok := spec.Type.(*ast.StructType); ok {
|
||||
// if _, err := fmt.Fprintf(&buf, " class %s {\n", spec.Name.Name); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// for _, field := range structType.Fields.List {
|
||||
//
|
||||
// // Ignore field names that have a lower case first letter
|
||||
// if !unicode.IsUpper(rune(field.Names[0].Name[0])) {
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// // Get the Go type of the field
|
||||
// goType := types.ExprString(field.Type)
|
||||
// // Check if the type is an array
|
||||
// if arrayType, ok := field.Type.(*ast.ArrayType); ok {
|
||||
// // Get the element type of the array
|
||||
// elementType := types.ExprString(arrayType.Elt)
|
||||
// // Look up the corresponding TypeScript type
|
||||
// tsType, ok := goToTS[elementType]
|
||||
// if !ok {
|
||||
// // strip off the * prefix if it is there
|
||||
// if strings.HasPrefix(elementType, "*") {
|
||||
// elementType = elementType[1:]
|
||||
// }
|
||||
// if knownStructs.exists(elementType) {
|
||||
// tsType = elementType
|
||||
// } else {
|
||||
// tsType = "any"
|
||||
// }
|
||||
// }
|
||||
// // Output the field as an array of the corresponding TypeScript type
|
||||
// if _, err := fmt.Fprintf(&buf, " %s: %s[];\n", field.Names[0].Name, tsType); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// } else {
|
||||
// // strip off the * prefix if it is there
|
||||
// if strings.HasPrefix(goType, "*") {
|
||||
// goType = goType[1:]
|
||||
// }
|
||||
// // Look up the corresponding TypeScript type
|
||||
// tsType, ok := goToTS[goType]
|
||||
// if !ok {
|
||||
// if knownStructs.exists(goType) {
|
||||
// tsType = goType
|
||||
// } else {
|
||||
// tsType = "any"
|
||||
// }
|
||||
// }
|
||||
// // Output the field as the corresponding TypeScript type
|
||||
// if _, err := fmt.Fprintf(&buf, " %s: %s;\n", field.Names[0].Name, tsType); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if _, err := fmt.Fprintf(&buf, " }\n"); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if _, err := fmt.Fprintf(&buf, "}\n\n"); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// }
|
||||
// return buf.Bytes(), nil
|
||||
//}
|
||||
|
@ -2,6 +2,7 @@ package parser
|
||||
|
||||
import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/wailsapp/wails/v3/internal/flags"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -15,40 +16,39 @@ func TestGenerateModels(t *testing.T) {
|
||||
want string
|
||||
}{
|
||||
{
|
||||
"testdata/function_single",
|
||||
"",
|
||||
dir: "testdata/function_single",
|
||||
},
|
||||
{
|
||||
"testdata/function_from_imported_package",
|
||||
getFile("testdata/function_from_imported_package/models.ts"),
|
||||
dir: "testdata/function_from_imported_package",
|
||||
want: getFile("testdata/function_from_imported_package/models.ts"),
|
||||
},
|
||||
{
|
||||
"testdata/variable_single",
|
||||
"",
|
||||
dir: "testdata/variable_single",
|
||||
},
|
||||
{
|
||||
"testdata/variable_single_from_function",
|
||||
"",
|
||||
dir: "testdata/variable_single_from_function",
|
||||
},
|
||||
{
|
||||
"testdata/variable_single_from_other_function",
|
||||
getFile("testdata/variable_single_from_other_function/models.ts"),
|
||||
dir: "testdata/variable_single_from_other_function",
|
||||
want: getFile("testdata/variable_single_from_other_function/models.ts"),
|
||||
},
|
||||
{
|
||||
"testdata/struct_literal_single",
|
||||
getFile("testdata/struct_literal_single/models.ts"),
|
||||
dir: "testdata/struct_literal_single",
|
||||
want: getFile("testdata/struct_literal_single/models.ts"),
|
||||
},
|
||||
{
|
||||
"testdata/struct_literal_multiple",
|
||||
"",
|
||||
dir: "testdata/struct_literal_multiple",
|
||||
},
|
||||
{
|
||||
"testdata/struct_literal_multiple_other",
|
||||
getFile("testdata/struct_literal_multiple_other/models.ts"),
|
||||
dir: "testdata/struct_literal_multiple_other",
|
||||
want: getFile("testdata/struct_literal_multiple_other/models.ts"),
|
||||
},
|
||||
{
|
||||
"testdata/struct_literal_multiple_files",
|
||||
"",
|
||||
dir: "testdata/struct_literal_multiple_files",
|
||||
},
|
||||
{
|
||||
dir: "testdata/enum",
|
||||
want: getFile("testdata/enum/models.ts"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@ -60,7 +60,7 @@ func TestGenerateModels(t *testing.T) {
|
||||
}
|
||||
|
||||
// Generate Models
|
||||
got, err := GenerateModels(project.Models)
|
||||
got, err := GenerateModels(project.Models, project.Types, &flags.GenerateBindingsOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateModels() error = %v", err)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v3/internal/flags"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
@ -12,6 +13,7 @@ import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/wailsapp/wails/v3/internal/hash"
|
||||
@ -26,16 +28,39 @@ type StructDef struct {
|
||||
Fields []*Field
|
||||
}
|
||||
|
||||
func (s *StructDef) DefaultValueList() string {
|
||||
var allFields []string
|
||||
for _, field := range s.Fields {
|
||||
thisFieldWithDefaultValue := fmt.Sprintf("%s = %s", field.JSName(), field.DefaultValue())
|
||||
allFields = append(allFields, thisFieldWithDefaultValue)
|
||||
}
|
||||
return strings.Join(allFields, ", ")
|
||||
}
|
||||
|
||||
type ParameterType struct {
|
||||
Name string
|
||||
IsStruct bool
|
||||
IsSlice bool
|
||||
IsPointer bool
|
||||
IsEnum bool
|
||||
MapKey *ParameterType
|
||||
MapValue *ParameterType
|
||||
Package string
|
||||
}
|
||||
|
||||
type EnumDef struct {
|
||||
Name string
|
||||
Filename string
|
||||
DocComments []string
|
||||
Values []*EnumValue
|
||||
}
|
||||
|
||||
type EnumValue struct {
|
||||
Name string
|
||||
Value string
|
||||
DocComments []string
|
||||
}
|
||||
|
||||
type Parameter struct {
|
||||
Name string
|
||||
Type *ParameterType
|
||||
@ -73,7 +98,7 @@ func (p *Parameter) JSType() string {
|
||||
}
|
||||
|
||||
// if the type is a struct, we need to add the package name
|
||||
if p.Type.IsStruct {
|
||||
if p.Type.IsStruct || p.Type.IsEnum {
|
||||
typeName = p.NamespacedStructType()
|
||||
typeName = strings.ReplaceAll(typeName, ".", "")
|
||||
}
|
||||
@ -117,7 +142,7 @@ func (f *Field) JSName() string {
|
||||
// via assignment for simple types or constructors for structs
|
||||
func (f *Field) TSBuild(pkg string) string {
|
||||
if !f.Type.IsStruct {
|
||||
return fmt.Sprintf("source['%s']", f.JSName())
|
||||
return fmt.Sprintf("safeSource['%s']", f.JSName())
|
||||
}
|
||||
|
||||
if f.Type.Package == "" || f.Type.Package == pkg {
|
||||
@ -141,7 +166,8 @@ func (f *Field) JSDef(pkg string) string {
|
||||
}
|
||||
|
||||
var result string
|
||||
if f.Type.Package == "" || f.Type.Package == pkg {
|
||||
isExternalStruct := f.Type.Package != "" && f.Type.Package != pkg && f.Type.IsStruct
|
||||
if f.Type.Package == "" || f.Type.Package == pkg || !isExternalStruct {
|
||||
result += fmt.Sprintf("%s: %s;", f.JSName(), jsType)
|
||||
} else {
|
||||
parts := strings.Split(f.Type.Package, "/")
|
||||
@ -155,12 +181,45 @@ func (f *Field) JSDef(pkg string) string {
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *Field) DefaultValue() string {
|
||||
// Return the default value of the typescript version of the type as a string
|
||||
switch f.Type.Name {
|
||||
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uintptr", "float32", "float64", "uint64":
|
||||
return "0"
|
||||
case "string":
|
||||
return `""`
|
||||
case "bool":
|
||||
return "false"
|
||||
default:
|
||||
return "null"
|
||||
}
|
||||
}
|
||||
|
||||
type ConstDef struct {
|
||||
Name string
|
||||
DocComment string
|
||||
Value string
|
||||
}
|
||||
|
||||
type TypeDef struct {
|
||||
Name string
|
||||
DocComments []string
|
||||
Type string
|
||||
Consts []*ConstDef
|
||||
ShouldGenerate bool
|
||||
}
|
||||
|
||||
func (t *TypeDef) GeneratedName() string {
|
||||
return t.Name + "Enum"
|
||||
}
|
||||
|
||||
type ParsedPackage struct {
|
||||
Pkg *ast.Package
|
||||
Name string
|
||||
Path string
|
||||
Dir string
|
||||
StructCache map[structName]*StructDef
|
||||
TypeCache map[string]*TypeDef
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
@ -168,7 +227,19 @@ type Project struct {
|
||||
Path string
|
||||
BoundMethods map[packagePath]map[structName][]*BoundMethod
|
||||
Models map[packagePath]map[structName]*StructDef
|
||||
Types map[packagePath]map[structName]*TypeDef
|
||||
anonymousStructIDCounter int
|
||||
Stats Stats
|
||||
}
|
||||
|
||||
type Stats struct {
|
||||
NumPackages int
|
||||
NumStructs int
|
||||
NumMethods int
|
||||
NumEnums int
|
||||
NumModels int
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
}
|
||||
|
||||
func ParseProject(projectPath string) (*Project, error) {
|
||||
@ -176,6 +247,7 @@ func ParseProject(projectPath string) (*Project, error) {
|
||||
BoundMethods: make(map[packagePath]map[structName][]*BoundMethod),
|
||||
packageCache: make(map[string]*ParsedPackage),
|
||||
}
|
||||
result.Stats.StartTime = time.Now()
|
||||
pkgs, err := result.parseDirectory(projectPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -195,8 +267,8 @@ func ParseProject(projectPath string) (*Project, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GenerateBindingsAndModels(projectDir string, outputDir string) error {
|
||||
p, err := ParseProject(projectDir)
|
||||
func GenerateBindingsAndModels(options *flags.GenerateBindingsOptions) error {
|
||||
p, err := ParseProject(options.ProjectDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -204,37 +276,51 @@ func GenerateBindingsAndModels(projectDir string, outputDir string) error {
|
||||
if p.BoundMethods == nil {
|
||||
return nil
|
||||
}
|
||||
err = os.MkdirAll(outputDir, 0755)
|
||||
err = os.MkdirAll(options.OutputDirectory, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Stats.NumMethods = len(p.BoundMethods)
|
||||
generatedMethods := GenerateBindings(p.BoundMethods)
|
||||
for pkg, text := range generatedMethods {
|
||||
// Write the file
|
||||
err = os.WriteFile(filepath.Join(outputDir, "bindings_"+pkg+".js"), []byte(text), 0644)
|
||||
err = os.WriteFile(filepath.Join(options.OutputDirectory, pkg+".js"), []byte(text), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
p.Stats.NumModels = len(p.Models)
|
||||
p.Stats.NumEnums = len(p.Types)
|
||||
|
||||
// Generate Models
|
||||
if len(p.Models) > 0 {
|
||||
generatedModels, err := GenerateModels(p.Models)
|
||||
generatedModels, err := GenerateModels(p.Models, p.Types, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(outputDir, "models.ts"), []byte(generatedModels), 0644)
|
||||
err = os.WriteFile(filepath.Join(options.OutputDirectory, options.ModelsFilename), []byte(generatedModels), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
absPath, err := filepath.Abs(projectDir)
|
||||
p.Stats.EndTime = time.Now()
|
||||
|
||||
absPath, err := filepath.Abs(options.ProjectDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println("Generated bindings and models for project: " + absPath)
|
||||
absPath, err = filepath.Abs(outputDir)
|
||||
|
||||
fmt.Printf("Processed: %s, %s, %s, %s, %s in %s.\n",
|
||||
pluralise(p.Stats.NumPackages, "Package"),
|
||||
pluralise(p.Stats.NumStructs, "Struct"),
|
||||
pluralise(p.Stats.NumMethods, "Method"),
|
||||
pluralise(p.Stats.NumEnums, "Enum"),
|
||||
pluralise(p.Stats.NumModels, "Model"),
|
||||
p.Stats.EndTime.Sub(p.Stats.StartTime).String())
|
||||
|
||||
absPath, err = filepath.Abs(options.OutputDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -243,6 +329,13 @@ func GenerateBindingsAndModels(projectDir string, outputDir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func pluralise(number int, word string) string {
|
||||
if number == 1 {
|
||||
return fmt.Sprintf("%d %s", number, word)
|
||||
}
|
||||
return fmt.Sprintf("%d %ss", number, word)
|
||||
}
|
||||
|
||||
func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error) {
|
||||
if p.packageCache[dir] != nil {
|
||||
return map[string]*ParsedPackage{dir: p.packageCache[dir]}, nil
|
||||
@ -261,6 +354,7 @@ func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error)
|
||||
return nil, err
|
||||
}
|
||||
var result = make(map[string]*ParsedPackage)
|
||||
p.Stats.NumPackages = len(pkgs)
|
||||
for packageName, pkg := range pkgs {
|
||||
parsedPackage := &ParsedPackage{
|
||||
Pkg: pkg,
|
||||
@ -268,6 +362,7 @@ func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error)
|
||||
Path: packageName,
|
||||
Dir: getDirectoryForPackage(pkg),
|
||||
StructCache: make(map[structName]*StructDef),
|
||||
TypeCache: make(map[string]*TypeDef),
|
||||
}
|
||||
p.packageCache[packageName] = parsedPackage
|
||||
result[packageName] = parsedPackage
|
||||
@ -285,6 +380,28 @@ func (p *Project) findApplicationNewCalls(pkgs map[string]*ParsedPackage) (err e
|
||||
for _, file := range thisPackage.Files {
|
||||
// Use an ast.Inspector to find the calls to application.New
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
// Check for const declaration
|
||||
genDecl, ok := n.(*ast.GenDecl)
|
||||
if ok {
|
||||
switch genDecl.Tok {
|
||||
case token.TYPE:
|
||||
var comments []string
|
||||
if genDecl.Doc != nil {
|
||||
comments = CommentGroupToText(genDecl.Doc)
|
||||
}
|
||||
for _, spec := range genDecl.Specs {
|
||||
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
||||
p.parseTypeDeclaration(typeSpec, pkg, comments)
|
||||
}
|
||||
}
|
||||
case token.CONST:
|
||||
p.parseConstDeclaration(genDecl, pkg)
|
||||
default:
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// Check if the node is a call expression
|
||||
callExpr, ok := n.(*ast.CallExpr)
|
||||
if !ok {
|
||||
@ -372,6 +489,7 @@ func (p *Project) findApplicationNewCalls(pkgs map[string]*ParsedPackage) (err e
|
||||
return true
|
||||
})
|
||||
}
|
||||
p.addTypes(pkg.Path, pkg.TypeCache)
|
||||
if !callFound {
|
||||
return fmt.Errorf("no Bound structs found")
|
||||
}
|
||||
@ -443,7 +561,7 @@ func (p *Project) parseBoundStructMethods(name string, pkg *ParsedPackage) error
|
||||
method := &BoundMethod{
|
||||
ID: id,
|
||||
Name: funcDecl.Name.Name,
|
||||
DocComment: funcDecl.Doc.Text(),
|
||||
DocComment: strings.TrimSpace(funcDecl.Doc.Text()),
|
||||
Alias: alias,
|
||||
}
|
||||
|
||||
@ -464,6 +582,20 @@ func (p *Project) parseBoundStructMethods(name string, pkg *ParsedPackage) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) addTypes(packagePath string, types map[string]*TypeDef) {
|
||||
if types == nil || len(types) == 0 {
|
||||
return
|
||||
}
|
||||
if p.Types == nil {
|
||||
p.Types = make(map[string]map[string]*TypeDef)
|
||||
}
|
||||
_, ok := p.Types[packagePath]
|
||||
if !ok {
|
||||
p.Types[packagePath] = make(map[string]*TypeDef)
|
||||
}
|
||||
p.Types[packagePath] = types
|
||||
}
|
||||
|
||||
func (p *Project) parseParameters(params *ast.FieldList, pkg *ParsedPackage) []*Parameter {
|
||||
var result []*Parameter
|
||||
for _, field := range params.List {
|
||||
@ -489,11 +621,21 @@ func (p *Project) parseParameters(params *ast.FieldList, pkg *ParsedPackage) []*
|
||||
}
|
||||
|
||||
func (p *Project) parseParameterType(field *ast.Field, pkg *ParsedPackage) *ParameterType {
|
||||
result := &ParameterType{}
|
||||
result := &ParameterType{
|
||||
Package: pkg.Path,
|
||||
}
|
||||
result.Name = getTypeString(field.Type)
|
||||
switch t := field.Type.(type) {
|
||||
case *ast.Ident:
|
||||
result.IsStruct = isStructType(t)
|
||||
if !result.IsStruct {
|
||||
// Check if it's a type alias
|
||||
typeDef, ok := pkg.TypeCache[t.Name]
|
||||
if ok {
|
||||
typeDef.ShouldGenerate = true
|
||||
result.IsEnum = true
|
||||
}
|
||||
}
|
||||
case *ast.StarExpr:
|
||||
result = p.parseParameterType(&ast.Field{Type: t.X}, pkg)
|
||||
result.IsPointer = true
|
||||
@ -597,9 +739,9 @@ func (p *Project) parseStructFields(structType *ast.StructType, pkg *ParsedPacka
|
||||
if !ok {
|
||||
p.getStructDef(paramType.Name, pkg)
|
||||
}
|
||||
if paramType.Package == "" {
|
||||
paramType.Package = pkg.Path
|
||||
}
|
||||
}
|
||||
if paramType.Package == "" {
|
||||
paramType.Package = pkg.Path
|
||||
}
|
||||
thisField.Type = paramType
|
||||
result = append(result, thisField)
|
||||
@ -822,6 +964,72 @@ func (p *Project) getFunctionFromName(name string, parsedPackage *ParsedPackage)
|
||||
return nil, fmt.Errorf("function not found")
|
||||
}
|
||||
|
||||
func (p *Project) parseTypeDeclaration(decl *ast.TypeSpec, pkg *ParsedPackage, comments []string) {
|
||||
switch t := decl.Type.(type) {
|
||||
case *ast.Ident:
|
||||
switch t.Name {
|
||||
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64",
|
||||
"uintptr", "float32", "float64", "string", "bool":
|
||||
// Store this in the type cache
|
||||
pkg.TypeCache[decl.Name.Name] = &TypeDef{
|
||||
Name: decl.Name.Name,
|
||||
Type: t.Name,
|
||||
DocComments: TrimSlice(comments),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Project) parseConstDeclaration(decl *ast.GenDecl, pkg *ParsedPackage) {
|
||||
// Check if the type of the constant is in the type cache and if it doesn't exist, return
|
||||
var latestValues []ast.Expr
|
||||
|
||||
for _, spec := range decl.Specs {
|
||||
valueSpec, ok := spec.(*ast.ValueSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// Extract the Type
|
||||
typeString := getTypeString(valueSpec.Type)
|
||||
if typeString == "" {
|
||||
continue
|
||||
}
|
||||
// Check if the type is in the type cache
|
||||
typeDecl, ok := pkg.TypeCache[typeString]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the latest values
|
||||
if len(valueSpec.Values) > 0 {
|
||||
latestValues = valueSpec.Values
|
||||
}
|
||||
|
||||
// Iterate over the names
|
||||
for index, name := range valueSpec.Names {
|
||||
constDecl := &ConstDef{
|
||||
Name: name.Name,
|
||||
Value: typeString,
|
||||
}
|
||||
|
||||
// Get the value
|
||||
if len(latestValues) > 0 {
|
||||
switch t := latestValues[index].(type) {
|
||||
case *ast.BasicLit:
|
||||
constDecl.Value = t.Value
|
||||
case *ast.Ident:
|
||||
constDecl.Value = t.Name
|
||||
}
|
||||
}
|
||||
|
||||
if valueSpec.Doc != nil {
|
||||
constDecl.DocComment = strings.TrimSpace(valueSpec.Doc.Text())
|
||||
}
|
||||
typeDecl.Consts = append(typeDecl.Consts, constDecl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getTypeString(expr ast.Expr) string {
|
||||
switch t := expr.(type) {
|
||||
case *ast.Ident:
|
||||
@ -852,7 +1060,10 @@ func isStructType(expr ast.Expr) bool {
|
||||
case *ast.SliceExpr:
|
||||
return isStructType(e.X)
|
||||
case *ast.Ident:
|
||||
return e.Obj != nil && e.Obj.Kind == ast.Typ
|
||||
if e.Obj != nil && e.Obj.Kind == ast.Typ {
|
||||
return isStructType(e.Obj.Decl.(*ast.TypeSpec).Type)
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@ -869,3 +1080,21 @@ func getDirectoryForPackage(pkg *ast.Package) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func CommentGroupToText(comments *ast.CommentGroup) []string {
|
||||
if comments == nil {
|
||||
return nil
|
||||
}
|
||||
var result []string
|
||||
for _, comment := range comments.List {
|
||||
result = append(result, strings.TrimSpace(comment.Text))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func TrimSlice(comments []string) []string {
|
||||
for i, comment := range comments {
|
||||
comments[i] = strings.TrimSpace(comment)
|
||||
}
|
||||
return comments
|
||||
}
|
||||
|
162
v3/internal/parser/parser_enum_test.go
Normal file
162
v3/internal/parser/parser_enum_test.go
Normal file
@ -0,0 +1,162 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestParseEnum(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dir string
|
||||
wantBoundMethods map[string]map[string][]*BoundMethod
|
||||
wantEnums map[string]map[string]*EnumDef
|
||||
wantModels map[string]map[string]*StructDef
|
||||
wantTypes map[string]map[string]*TypeDef
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "should find a bound services with an enum",
|
||||
dir: "testdata/enum",
|
||||
wantErr: false,
|
||||
wantBoundMethods: map[string]map[string][]*BoundMethod{
|
||||
"main": {
|
||||
"GreetService": {
|
||||
{
|
||||
Name: "Greet",
|
||||
DocComment: "Greet does XYZ",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "title",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "Title",
|
||||
IsEnum: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1411160069,
|
||||
},
|
||||
{
|
||||
Name: "NewPerson",
|
||||
DocComment: "NewPerson creates a new person",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "Person",
|
||||
IsStruct: true,
|
||||
IsPointer: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1661412647,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantTypes: map[string]map[string]*TypeDef{
|
||||
"main": {
|
||||
"Title": {
|
||||
Name: "Title",
|
||||
Type: "string",
|
||||
Consts: []*ConstDef{
|
||||
{
|
||||
Name: "Mister",
|
||||
DocComment: "Mister is a title",
|
||||
Value: `"Mr"`,
|
||||
},
|
||||
{
|
||||
Name: "Miss",
|
||||
Value: `"Miss"`,
|
||||
},
|
||||
{
|
||||
Name: "Ms",
|
||||
Value: `"Ms"`,
|
||||
},
|
||||
{
|
||||
Name: "Mrs",
|
||||
Value: `"Mrs"`,
|
||||
},
|
||||
{
|
||||
Name: "Dr",
|
||||
Value: `"Dr"`,
|
||||
},
|
||||
},
|
||||
ShouldGenerate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantModels: map[string]map[string]*StructDef{
|
||||
"main": {
|
||||
"Person": {
|
||||
Name: "Person",
|
||||
Fields: []*Field{
|
||||
{
|
||||
Name: "Title",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "Title",
|
||||
IsEnum: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseProject(tt.dir)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.wantModels, got.Models) {
|
||||
t.Errorf("ParseDirectory() failed:\n" + cmp.Diff(tt.wantModels, got.Models))
|
||||
}
|
||||
if diff := cmp.Diff(tt.wantTypes, got.Types); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
204
v3/internal/parser/parser_function_test.go
Normal file
204
v3/internal/parser/parser_function_test.go
Normal file
@ -0,0 +1,204 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestParseFunction(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dir string
|
||||
wantBoundMethods map[string]map[string][]*BoundMethod
|
||||
wantEnums map[string]map[string]*EnumDef
|
||||
wantModels map[string]map[string]*StructDef
|
||||
wantTypes map[string]map[string]*TypeDef
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "should find a bound service returned from a function call",
|
||||
dir: "testdata/function_single",
|
||||
wantErr: false,
|
||||
wantBoundMethods: map[string]map[string][]*BoundMethod{
|
||||
"main": {
|
||||
"GreetService": {
|
||||
{
|
||||
Name: "Greet",
|
||||
DocComment: "Greet someone",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1411160069,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should find a bound service returned from a function call in another package",
|
||||
dir: "testdata/function_from_imported_package",
|
||||
wantErr: false,
|
||||
wantBoundMethods: map[string]map[string][]*BoundMethod{
|
||||
"main": {
|
||||
"GreetService": {
|
||||
{
|
||||
Name: "Greet",
|
||||
DocComment: "Greet does XYZ",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1411160069,
|
||||
},
|
||||
{
|
||||
Name: "NewPerson",
|
||||
DocComment: "NewPerson creates a new person",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "Person",
|
||||
IsPointer: true,
|
||||
IsStruct: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1661412647,
|
||||
},
|
||||
},
|
||||
},
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services": {
|
||||
"OtherService": {
|
||||
{
|
||||
Name: "Yay",
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
Name: "Address",
|
||||
IsStruct: true,
|
||||
IsPointer: true,
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 302702907,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantModels: map[string]map[string]*StructDef{
|
||||
"main": {
|
||||
"Person": {
|
||||
Name: "Person",
|
||||
Fields: []*Field{
|
||||
{
|
||||
Name: "Name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Address",
|
||||
Type: &ParameterType{
|
||||
Name: "Address",
|
||||
IsStruct: true,
|
||||
IsPointer: true,
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services": {
|
||||
"Address": {
|
||||
Name: "Address",
|
||||
Fields: []*Field{
|
||||
{
|
||||
Name: "Street",
|
||||
Type: &ParameterType{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "State",
|
||||
Type: &ParameterType{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Country",
|
||||
Type: &ParameterType{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseProject(tt.dir)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.wantModels, got.Models) {
|
||||
t.Errorf("ParseDirectory() failed:\n" + cmp.Diff(tt.wantModels, got.Models))
|
||||
}
|
||||
if diff := cmp.Diff(tt.wantTypes, got.Types); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
250
v3/internal/parser/parser_struct_literal_multiple_test.go
Normal file
250
v3/internal/parser/parser_struct_literal_multiple_test.go
Normal file
@ -0,0 +1,250 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestParseStructLiteralMultiple(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dir string
|
||||
wantBoundMethods map[string]map[string][]*BoundMethod
|
||||
wantEnums map[string]map[string]*EnumDef
|
||||
wantModels map[string]map[string]*StructDef
|
||||
wantTypes map[string]map[string]*TypeDef
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "should find multiple bound services",
|
||||
dir: "testdata/struct_literal_multiple",
|
||||
wantBoundMethods: map[string]map[string][]*BoundMethod{
|
||||
"main": {
|
||||
"GreetService": {
|
||||
{
|
||||
Name: "Greet",
|
||||
DocComment: "",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1411160069,
|
||||
},
|
||||
},
|
||||
"OtherService": {
|
||||
{
|
||||
Name: "Hello",
|
||||
ID: 4249972365,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should find multiple bound services over multiple files",
|
||||
dir: "testdata/struct_literal_multiple_files",
|
||||
wantBoundMethods: map[string]map[string][]*BoundMethod{
|
||||
"main": {
|
||||
"GreetService": {
|
||||
{
|
||||
Name: "Greet",
|
||||
DocComment: "",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1411160069,
|
||||
},
|
||||
},
|
||||
"OtherService": {
|
||||
{
|
||||
Name: "Hello",
|
||||
ID: 4249972365,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should find multiple bound services over multiple packages",
|
||||
dir: "testdata/struct_literal_multiple_other",
|
||||
wantErr: false,
|
||||
wantBoundMethods: map[string]map[string][]*BoundMethod{
|
||||
"main": {
|
||||
"GreetService": {
|
||||
{
|
||||
Name: "Greet",
|
||||
DocComment: "Greet does XYZ",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1411160069,
|
||||
},
|
||||
{
|
||||
Name: "NewPerson",
|
||||
DocComment: "NewPerson creates a new person",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "Person",
|
||||
IsPointer: true,
|
||||
IsStruct: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1661412647,
|
||||
},
|
||||
},
|
||||
},
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services": {
|
||||
"OtherService": {
|
||||
{
|
||||
Name: "Yay",
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
Name: "Address",
|
||||
IsStruct: true,
|
||||
IsPointer: true,
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 469445984,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantModels: map[string]map[string]*StructDef{
|
||||
"main": {
|
||||
"Person": {
|
||||
Name: "Person",
|
||||
Fields: []*Field{
|
||||
{
|
||||
Name: "Name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
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{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "State",
|
||||
Type: &ParameterType{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Country",
|
||||
Type: &ParameterType{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseProject(tt.dir)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.wantModels, got.Models) {
|
||||
t.Errorf("ParseDirectory() failed:\n" + cmp.Diff(tt.wantModels, got.Models))
|
||||
}
|
||||
if diff := cmp.Diff(tt.wantTypes, got.Types); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
1091
v3/internal/parser/parser_struct_literal_non_pointer_single_test.go
Normal file
1091
v3/internal/parser/parser_struct_literal_non_pointer_single_test.go
Normal file
File diff suppressed because it is too large
Load Diff
1092
v3/internal/parser/parser_struct_literal_single_test.go
Normal file
1092
v3/internal/parser/parser_struct_literal_single_test.go
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
238
v3/internal/parser/parser_variable_single_test.go
Normal file
238
v3/internal/parser/parser_variable_single_test.go
Normal file
@ -0,0 +1,238 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestParseVariableSingle(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dir string
|
||||
wantBoundMethods map[string]map[string][]*BoundMethod
|
||||
wantEnums map[string]map[string]*EnumDef
|
||||
wantModels map[string]map[string]*StructDef
|
||||
wantTypes map[string]map[string]*TypeDef
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "should find a bound services using a variable",
|
||||
dir: "testdata/variable_single",
|
||||
wantErr: false,
|
||||
wantBoundMethods: map[string]map[string][]*BoundMethod{
|
||||
"main": {
|
||||
"GreetService": {
|
||||
{
|
||||
Name: "Greet",
|
||||
DocComment: "Greet someone",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1411160069,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should find a bound services using a variable from function call",
|
||||
dir: "testdata/variable_single_from_function",
|
||||
wantErr: false,
|
||||
wantBoundMethods: map[string]map[string][]*BoundMethod{
|
||||
"main": {
|
||||
"GreetService": {
|
||||
{
|
||||
Name: "Greet",
|
||||
DocComment: "Greet someone",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1411160069,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should find a bound services using a variable from function call in another package",
|
||||
dir: "testdata/variable_single_from_other_function",
|
||||
wantErr: false,
|
||||
wantBoundMethods: map[string]map[string][]*BoundMethod{
|
||||
"main": {
|
||||
"GreetService": {
|
||||
{
|
||||
Name: "Greet",
|
||||
DocComment: "Greet does XYZ",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1411160069,
|
||||
},
|
||||
{
|
||||
Name: "NewPerson",
|
||||
DocComment: "NewPerson creates a new person",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "Person",
|
||||
IsPointer: true,
|
||||
IsStruct: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1661412647,
|
||||
},
|
||||
},
|
||||
},
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services": {
|
||||
"OtherService": {
|
||||
{
|
||||
Name: "Yay",
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
Name: "Address",
|
||||
IsStruct: true,
|
||||
IsPointer: true,
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 302702907,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantModels: map[string]map[string]*StructDef{
|
||||
"main": {
|
||||
"Person": {
|
||||
Name: "Person",
|
||||
Fields: []*Field{
|
||||
{
|
||||
Name: "Name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Address",
|
||||
Type: &ParameterType{
|
||||
Name: "Address",
|
||||
IsStruct: true,
|
||||
IsPointer: true,
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services": {
|
||||
"Address": {
|
||||
Name: "Address",
|
||||
Fields: []*Field{
|
||||
{
|
||||
Name: "Street",
|
||||
Type: &ParameterType{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "State",
|
||||
Type: &ParameterType{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Country",
|
||||
Type: &ParameterType{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseProject(tt.dir)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.wantModels, got.Models) {
|
||||
t.Errorf("ParseDirectory() failed:\n" + cmp.Diff(tt.wantModels, got.Models))
|
||||
}
|
||||
if diff := cmp.Diff(tt.wantTypes, got.Types); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
export class {{.Name}} {
|
||||
{{range .Fields}}{{.}}
|
||||
{{end}}
|
||||
static createFrom(source: any = {}) {
|
||||
return new {{.Name}}(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) {
|
||||
source = JSON.parse(source);
|
||||
}
|
||||
|
||||
{{range .Fields}}this.{{jsName .}} = source["{{jsName .}}"]
|
||||
{{end}}
|
||||
}
|
||||
}
|
8
v3/internal/parser/templates/interfaces.ts.tmpl
Normal file
8
v3/internal/parser/templates/interfaces.ts.tmpl
Normal file
@ -0,0 +1,8 @@
|
||||
{{$pkg := .Package}}
|
||||
export namespace {{.Package}} {
|
||||
{{range $name, $def := .Models}}
|
||||
export interface {{$def.Name}} { {{range $def.Fields}}
|
||||
{{.JSDef $pkg}}{{end}}
|
||||
}
|
||||
{{end}}
|
||||
}
|
@ -1,21 +1,31 @@
|
||||
{{$pkg := .Package}}
|
||||
export namespace {{.Package}} {
|
||||
{{range $name, $def := .Models}}
|
||||
export class {{$def.Name}} {
|
||||
{{range $def.Fields}}{{.JSDef $pkg}}
|
||||
{{range $enumindex, $enumdef := .Enums}}
|
||||
{{- range $commentindex, $commentdef := .DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- end}}
|
||||
export enum {{$enumdef.Name}} {
|
||||
{{- range $constindex, $constdef := .Consts}}
|
||||
{{- if $constdef.DocComment}}
|
||||
// {{$constdef.DocComment}}
|
||||
{{- end}}
|
||||
{{$constdef.Name}} = {{$constdef.Value}},{{end}}
|
||||
}
|
||||
{{- end}}
|
||||
{{range $name, $def := .Models}}
|
||||
export class {{$def.Name}} {
|
||||
{{range $def.Fields}}{{.JSDef $pkg}}
|
||||
{{end}}
|
||||
constructor(source: Partial<{{$def.Name}}> = {}) {
|
||||
const { {{$def.DefaultValueList}} } = source; {{range $def.Fields}}
|
||||
this.{{.JSName}} = {{.JSName}};{{end}}
|
||||
}
|
||||
|
||||
static createFrom(source: string | object = {}): {{$def.Name}} {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new {{$def.Name}}(parsedSource as Partial<{{$def.Name}}>);
|
||||
}
|
||||
|
||||
}
|
||||
{{end}}
|
||||
static createFrom(source: any = {}) {
|
||||
return new {{$def.Name}}(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) {
|
||||
source = JSON.parse(source);
|
||||
}
|
||||
|
||||
{{range $def.Fields}}this.{{.JSName}} = {{.TSBuild $pkg}};
|
||||
{{end}}
|
||||
}
|
||||
}
|
||||
{{end}}
|
||||
}
|
||||
}
|
29
v3/internal/parser/testdata/enum/bindings_main.js
vendored
Normal file
29
v3/internal/parser/testdata/enum/bindings_main.js
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* @typedef {import('./models').main.Title} mainTitle
|
||||
* @typedef {import('./models').main.Person} mainPerson
|
||||
*/
|
||||
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet does XYZ
|
||||
* @param name {string}
|
||||
* @param title {mainTitle}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name, title) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
|
||||
/**
|
||||
* GreetService.NewPerson
|
||||
* NewPerson creates a new person
|
||||
* @param name {string}
|
||||
* @returns {Promise<mainPerson>}
|
||||
**/
|
||||
NewPerson: function(name) { return wails.CallByID(1661412647, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
};
|
||||
|
58
v3/internal/parser/testdata/enum/main.go
vendored
Normal file
58
v3/internal/parser/testdata/enum/main.go
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
type Title string
|
||||
|
||||
const (
|
||||
// Mister is a title
|
||||
Mister Title = "Mr"
|
||||
Miss Title = "Miss"
|
||||
Ms Title = "Ms"
|
||||
Mrs Title = "Mrs"
|
||||
Dr Title = "Dr"
|
||||
)
|
||||
|
||||
// GreetService is great
|
||||
type GreetService struct {
|
||||
SomeVariable int
|
||||
lowerCase string
|
||||
target *Person
|
||||
}
|
||||
|
||||
type Person struct {
|
||||
Title Title
|
||||
Name string
|
||||
}
|
||||
|
||||
// Greet does XYZ
|
||||
func (*GreetService) Greet(name string, title Title) string {
|
||||
return "Hello " + string(title) + " " + name
|
||||
}
|
||||
|
||||
// NewPerson creates a new person
|
||||
func (*GreetService) NewPerson(name string) *Person {
|
||||
return &Person{Name: name}
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := application.New(application.Options{
|
||||
Bind: []interface{}{
|
||||
&GreetService{},
|
||||
},
|
||||
})
|
||||
|
||||
app.NewWebviewWindow()
|
||||
|
||||
err := app.Run()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
33
v3/internal/parser/testdata/enum/models.ts
vendored
Normal file
33
v3/internal/parser/testdata/enum/models.ts
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export namespace main {
|
||||
|
||||
export enum Title {
|
||||
// Mister is a title
|
||||
Mister = "Mr",
|
||||
Miss = "Miss",
|
||||
Ms = "Ms",
|
||||
Mrs = "Mrs",
|
||||
Dr = "Dr",
|
||||
}
|
||||
|
||||
export class Person {
|
||||
title: Title;
|
||||
name: string;
|
||||
|
||||
constructor(source: Partial<Person> = {}) {
|
||||
const { title = null, name = "" } = source;
|
||||
this.title = title;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
static createFrom(source: string | object = {}): Person {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new Person(parsedSource as Partial<Person>);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
30
v3/internal/parser/testdata/enum_from_imported_package/bindings_main.js
vendored
Normal file
30
v3/internal/parser/testdata/enum_from_imported_package/bindings_main.js
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* @typedef {import('./models').main.Person} mainPerson
|
||||
*/
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.main = {
|
||||
GreetService: {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet does XYZ
|
||||
* @param name {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
|
||||
/**
|
||||
* GreetService.NewPerson
|
||||
* NewPerson creates a new person
|
||||
* @param name {string}
|
||||
* @returns {Promise<mainPerson>}
|
||||
**/
|
||||
NewPerson: function(name) { return wails.CallByID(1661412647, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
22
v3/internal/parser/testdata/enum_from_imported_package/bindings_services.js
vendored
Normal file
22
v3/internal/parser/testdata/enum_from_imported_package/bindings_services.js
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* @typedef {import('./models').services.Address} servicesAddress
|
||||
*/
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.services = {
|
||||
OtherService: {
|
||||
|
||||
/**
|
||||
* OtherService.Yay
|
||||
*
|
||||
*
|
||||
* @returns {Promise<servicesAddress>}
|
||||
**/
|
||||
Yay: function() { return wails.CallByID(302702907, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
37
v3/internal/parser/testdata/enum_from_imported_package/main.go
vendored
Normal file
37
v3/internal/parser/testdata/enum_from_imported_package/main.go
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/enum_from_imported_package/services"
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
// GreetService is great
|
||||
type GreetService struct {
|
||||
SomeVariable int
|
||||
lowerCase string
|
||||
}
|
||||
|
||||
// Greet does XYZ
|
||||
func (*GreetService) Greet(name string, title services.Title) string {
|
||||
return "Hello " + title.String() + " " + name
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := application.New(application.Options{
|
||||
Bind: []interface{}{
|
||||
&GreetService{},
|
||||
},
|
||||
})
|
||||
|
||||
app.NewWebviewWindow()
|
||||
|
||||
err := app.Run()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
47
v3/internal/parser/testdata/enum_from_imported_package/models.ts
vendored
Normal file
47
v3/internal/parser/testdata/enum_from_imported_package/models.ts
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export namespace main {
|
||||
|
||||
export class Person {
|
||||
name: string;
|
||||
address: services.Address;
|
||||
|
||||
constructor(source: Partial<Person> = {}) {
|
||||
const { name = "", address = null } = source;
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
static createFrom(source: string | object = {}): Person {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new Person(parsedSource as Partial<Person>);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace services {
|
||||
|
||||
export class Address {
|
||||
street: string;
|
||||
state: string;
|
||||
country: string;
|
||||
|
||||
constructor(source: Partial<Address> = {}) {
|
||||
const { street = "", state = "", country = "" } = source;
|
||||
this.street = street;
|
||||
this.state = state;
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
static createFrom(source: string | object = {}): Address {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new Address(parsedSource as Partial<Address>);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
16
v3/internal/parser/testdata/enum_from_imported_package/services/other.go
vendored
Normal file
16
v3/internal/parser/testdata/enum_from_imported_package/services/other.go
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
package services
|
||||
|
||||
type Title string
|
||||
|
||||
func (t Title) String() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
const (
|
||||
// Mister is a title
|
||||
Mister Title = "Mr"
|
||||
Miss Title = "Miss"
|
||||
Ms Title = "Ms"
|
||||
Mrs Title = "Mrs"
|
||||
Dr Title = "Dr"
|
||||
)
|
@ -6,15 +6,12 @@
|
||||
* @typedef {import('./models').main.Person} mainPerson
|
||||
*/
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.main = {
|
||||
GreetService: {
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet does XYZ
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -22,9 +19,9 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.NewPerson
|
||||
* NewPerson creates a new person
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<mainPerson>}
|
||||
**/
|
||||
NewPerson: function(name) { return wails.CallByID(1661412647, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -6,17 +6,14 @@
|
||||
* @typedef {import('./models').services.Address} servicesAddress
|
||||
*/
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.services = {
|
||||
OtherService: {
|
||||
export const OtherService = {
|
||||
|
||||
/**
|
||||
* OtherService.Yay
|
||||
*
|
||||
*
|
||||
*
|
||||
* @returns {Promise<servicesAddress>}
|
||||
**/
|
||||
Yay: function() { return wails.CallByID(302702907, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -3,49 +3,46 @@
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export namespace main {
|
||||
|
||||
export class Person {
|
||||
name: string;
|
||||
address: services.Address;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Person(source);
|
||||
}
|
||||
|
||||
export class Person {
|
||||
name: string;
|
||||
address: services.Address;
|
||||
|
||||
constructor(source: Partial<Person> = {}) {
|
||||
const { name = "", address = null } = source;
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) {
|
||||
source = JSON.parse(source);
|
||||
}
|
||||
static createFrom(source: string | object = {}): Person {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new Person(parsedSource as Partial<Person>);
|
||||
}
|
||||
|
||||
this.name = source['name'];
|
||||
this.address = services.Address.createFrom(source['address']);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export namespace services {
|
||||
|
||||
export class Address {
|
||||
street: string;
|
||||
state: string;
|
||||
country: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Address(source);
|
||||
}
|
||||
|
||||
export class Address {
|
||||
street: string;
|
||||
state: string;
|
||||
country: string;
|
||||
|
||||
constructor(source: Partial<Address> = {}) {
|
||||
const { street = "", state = "", country = "" } = source;
|
||||
this.street = street;
|
||||
this.state = state;
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) {
|
||||
source = JSON.parse(source);
|
||||
}
|
||||
static createFrom(source: string | object = {}): Address {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new Address(parsedSource as Partial<Address>);
|
||||
}
|
||||
|
||||
this.street = source['street'];
|
||||
this.state = source['state'];
|
||||
this.country = source['country'];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -2,17 +2,14 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.main = {
|
||||
GreetService: {
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet someone
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -2,27 +2,25 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.main = {
|
||||
GreetService: {
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
*
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
OtherService: {
|
||||
};
|
||||
|
||||
export const OtherService = {
|
||||
|
||||
/**
|
||||
* OtherService.Hello
|
||||
*
|
||||
*
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
**/
|
||||
Hello: function() { return wails.CallByID(4249972365, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -2,27 +2,25 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.main = {
|
||||
GreetService: {
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
*
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
OtherService: {
|
||||
};
|
||||
|
||||
export const OtherService = {
|
||||
|
||||
/**
|
||||
* OtherService.Hello
|
||||
*
|
||||
*
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
**/
|
||||
Hello: function() { return wails.CallByID(4249972365, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -6,15 +6,12 @@
|
||||
* @typedef {import('./models').main.Person} mainPerson
|
||||
*/
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.main = {
|
||||
GreetService: {
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet does XYZ
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -22,9 +19,9 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.NewPerson
|
||||
* NewPerson creates a new person
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<mainPerson>}
|
||||
**/
|
||||
NewPerson: function(name) { return wails.CallByID(1661412647, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -6,17 +6,14 @@
|
||||
* @typedef {import('./models').services.Address} servicesAddress
|
||||
*/
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.services = {
|
||||
OtherService: {
|
||||
export const OtherService = {
|
||||
|
||||
/**
|
||||
* OtherService.Yay
|
||||
*
|
||||
*
|
||||
*
|
||||
* @returns {Promise<servicesAddress>}
|
||||
**/
|
||||
Yay: function() { return wails.CallByID(469445984, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -3,49 +3,46 @@
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export namespace main {
|
||||
|
||||
export class Person {
|
||||
name: string;
|
||||
address: services.Address;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Person(source);
|
||||
}
|
||||
|
||||
export class Person {
|
||||
name: string;
|
||||
address: services.Address;
|
||||
|
||||
constructor(source: Partial<Person> = {}) {
|
||||
const { name = "", address = null } = source;
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) {
|
||||
source = JSON.parse(source);
|
||||
}
|
||||
static createFrom(source: string | object = {}): Person {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new Person(parsedSource as Partial<Person>);
|
||||
}
|
||||
|
||||
this.name = source['name'];
|
||||
this.address = services.Address.createFrom(source['address']);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export namespace services {
|
||||
|
||||
export class Address {
|
||||
street: string;
|
||||
state: string;
|
||||
country: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Address(source);
|
||||
}
|
||||
|
||||
export class Address {
|
||||
street: string;
|
||||
state: string;
|
||||
country: string;
|
||||
|
||||
constructor(source: Partial<Address> = {}) {
|
||||
const { street = "", state = "", country = "" } = source;
|
||||
this.street = street;
|
||||
this.state = state;
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) {
|
||||
source = JSON.parse(source);
|
||||
}
|
||||
static createFrom(source: string | object = {}): Address {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new Address(parsedSource as Partial<Address>);
|
||||
}
|
||||
|
||||
this.street = source['street'];
|
||||
this.state = source['state'];
|
||||
this.country = source['country'];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -6,15 +6,12 @@
|
||||
* @typedef {import('./models').main.Person} mainPerson
|
||||
*/
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.main = {
|
||||
GreetService: {
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.ArrayInt
|
||||
*
|
||||
* @param _in {number[]}
|
||||
* @param _in {number[]}
|
||||
* @returns {Promise<void>}
|
||||
**/
|
||||
ArrayInt: function(_in) { return wails.CallByID(3862002418, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -22,7 +19,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.BoolInBoolOut
|
||||
*
|
||||
* @param _in {boolean}
|
||||
* @param _in {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
**/
|
||||
BoolInBoolOut: function(_in) { return wails.CallByID(2424639793, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -30,7 +27,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.Float32InFloat32Out
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
Float32InFloat32Out: function(_in) { return wails.CallByID(3132595881, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -38,7 +35,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.Float64InFloat64Out
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
Float64InFloat64Out: function(_in) { return wails.CallByID(2182412247, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -46,7 +43,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet someone
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -54,7 +51,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.Int16InIntOut
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
Int16InIntOut: function(_in) { return wails.CallByID(3306292566, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -62,7 +59,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.Int16PointerInAndOutput
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
Int16PointerInAndOutput: function(_in) { return wails.CallByID(1754277916, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -70,7 +67,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.Int32InIntOut
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
Int32InIntOut: function(_in) { return wails.CallByID(1909469092, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -78,7 +75,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.Int32PointerInAndOutput
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
Int32PointerInAndOutput: function(_in) { return wails.CallByID(4251088558, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -86,7 +83,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.Int64InIntOut
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
Int64InIntOut: function(_in) { return wails.CallByID(1343888303, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -94,7 +91,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.Int64PointerInAndOutput
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
Int64PointerInAndOutput: function(_in) { return wails.CallByID(2205561041, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -102,7 +99,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.Int8InIntOut
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
Int8InIntOut: function(_in) { return wails.CallByID(572240879, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -110,7 +107,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.Int8PointerInAndOutput
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
Int8PointerInAndOutput: function(_in) { return wails.CallByID(2189402897, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -118,7 +115,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.IntInIntOut
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
IntInIntOut: function(_in) { return wails.CallByID(642881729, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -126,7 +123,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.IntPointerInAndOutput
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
IntPointerInAndOutput: function(_in) { return wails.CallByID(1066151743, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -134,7 +131,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.IntPointerInputNamedOutputs
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null, void>}
|
||||
**/
|
||||
IntPointerInputNamedOutputs: function(_in) { return wails.CallByID(2718999663, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -142,7 +139,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.MapIntInt
|
||||
*
|
||||
* @param _in {map}
|
||||
* @param _in {map}
|
||||
* @returns {Promise<void>}
|
||||
**/
|
||||
MapIntInt: function(_in) { return wails.CallByID(2386486356, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -150,7 +147,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.MapIntPointerInt
|
||||
*
|
||||
* @param _in {map}
|
||||
* @param _in {map}
|
||||
* @returns {Promise<void>}
|
||||
**/
|
||||
MapIntPointerInt: function(_in) { return wails.CallByID(550413585, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -158,7 +155,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.MapIntSliceInt
|
||||
*
|
||||
* @param _in {map}
|
||||
* @param _in {map}
|
||||
* @returns {Promise<void>}
|
||||
**/
|
||||
MapIntSliceInt: function(_in) { return wails.CallByID(2900172572, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -166,7 +163,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.MapIntSliceIntInMapIntSliceIntOut
|
||||
*
|
||||
* @param _in {map}
|
||||
* @param _in {map}
|
||||
* @returns {Promise<map>}
|
||||
**/
|
||||
MapIntSliceIntInMapIntSliceIntOut: function(_in) { return wails.CallByID(881980169, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -174,7 +171,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.NoInputsStringOut
|
||||
*
|
||||
*
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
NoInputsStringOut: function() { return wails.CallByID(1075577233, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -182,7 +179,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.PointerBoolInBoolOut
|
||||
*
|
||||
* @param _in {boolean | null}
|
||||
* @param _in {boolean | null}
|
||||
* @returns {Promise<boolean | null>}
|
||||
**/
|
||||
PointerBoolInBoolOut: function(_in) { return wails.CallByID(3589606958, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -190,7 +187,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.PointerFloat32InFloat32Out
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
PointerFloat32InFloat32Out: function(_in) { return wails.CallByID(224675106, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -198,7 +195,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.PointerFloat64InFloat64Out
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
PointerFloat64InFloat64Out: function(_in) { return wails.CallByID(2124953624, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -206,7 +203,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.PointerMapIntInt
|
||||
*
|
||||
* @param _in {map | null}
|
||||
* @param _in {map | null}
|
||||
* @returns {Promise<void>}
|
||||
**/
|
||||
PointerMapIntInt: function(_in) { return wails.CallByID(3516977899, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -214,7 +211,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.PointerStringInStringOut
|
||||
*
|
||||
* @param _in {string | null}
|
||||
* @param _in {string | null}
|
||||
* @returns {Promise<string | null>}
|
||||
**/
|
||||
PointerStringInStringOut: function(_in) { return wails.CallByID(229603958, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -222,7 +219,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.StringArrayInputNamedOutput
|
||||
*
|
||||
* @param _in {string[]}
|
||||
* @param _in {string[]}
|
||||
* @returns {Promise<string[]>}
|
||||
**/
|
||||
StringArrayInputNamedOutput: function(_in) { return wails.CallByID(3678582682, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -230,7 +227,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.StringArrayInputNamedOutputs
|
||||
*
|
||||
* @param _in {string[]}
|
||||
* @param _in {string[]}
|
||||
* @returns {Promise<string[], void>}
|
||||
**/
|
||||
StringArrayInputNamedOutputs: function(_in) { return wails.CallByID(319259595, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -238,7 +235,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.StringArrayInputStringArrayOut
|
||||
*
|
||||
* @param _in {string[]}
|
||||
* @param _in {string[]}
|
||||
* @returns {Promise<string[]>}
|
||||
**/
|
||||
StringArrayInputStringArrayOut: function(_in) { return wails.CallByID(383995060, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -246,7 +243,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.StringArrayInputStringOut
|
||||
*
|
||||
* @param _in {string[]}
|
||||
* @param _in {string[]}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
StringArrayInputStringOut: function(_in) { return wails.CallByID(1091960237, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -254,7 +251,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.StructInputStructOutput
|
||||
*
|
||||
* @param _in {mainPerson}
|
||||
* @param _in {mainPerson}
|
||||
* @returns {Promise<mainPerson>}
|
||||
**/
|
||||
StructInputStructOutput: function(_in) { return wails.CallByID(3835643147, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -262,7 +259,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.StructPointerInputErrorOutput
|
||||
*
|
||||
* @param _in {mainPerson | null}
|
||||
* @param _in {mainPerson | null}
|
||||
* @returns {Promise<void>}
|
||||
**/
|
||||
StructPointerInputErrorOutput: function(_in) { return wails.CallByID(2447692557, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -270,7 +267,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.StructPointerInputStructPointerOutput
|
||||
*
|
||||
* @param _in {mainPerson | null}
|
||||
* @param _in {mainPerson | null}
|
||||
* @returns {Promise<mainPerson>}
|
||||
**/
|
||||
StructPointerInputStructPointerOutput: function(_in) { return wails.CallByID(2943477349, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -278,7 +275,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.UInt16InUIntOut
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
UInt16InUIntOut: function(_in) { return wails.CallByID(3401034892, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -286,7 +283,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.UInt16PointerInAndOutput
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
UInt16PointerInAndOutput: function(_in) { return wails.CallByID(1236957573, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -294,7 +291,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.UInt32InUIntOut
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
UInt32InUIntOut: function(_in) { return wails.CallByID(1160383782, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -302,7 +299,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.UInt32PointerInAndOutput
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
UInt32PointerInAndOutput: function(_in) { return wails.CallByID(1739300671, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -310,7 +307,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.UInt64InUIntOut
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
UInt64InUIntOut: function(_in) { return wails.CallByID(793803239, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -318,7 +315,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.UInt64PointerInAndOutput
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
UInt64PointerInAndOutput: function(_in) { return wails.CallByID(1403757716, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -326,7 +323,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.UInt8InUIntOut
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
UInt8InUIntOut: function(_in) { return wails.CallByID(2988345717, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -334,7 +331,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.UInt8PointerInAndOutput
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
UInt8PointerInAndOutput: function(_in) { return wails.CallByID(518250834, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -342,7 +339,7 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.UIntInUIntOut
|
||||
*
|
||||
* @param _in {number}
|
||||
* @param _in {number}
|
||||
* @returns {Promise<number>}
|
||||
**/
|
||||
UIntInUIntOut: function(_in) { return wails.CallByID(2836661285, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -350,9 +347,9 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.UIntPointerInAndOutput
|
||||
*
|
||||
* @param _in {number | null}
|
||||
* @param _in {number | null}
|
||||
* @returns {Promise<number | null>}
|
||||
**/
|
||||
UIntPointerInAndOutput: function(_in) { return wails.CallByID(1367187362, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -3,62 +3,57 @@
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export namespace main {
|
||||
|
||||
export class Person {
|
||||
name: string;
|
||||
parent: Person;
|
||||
details: anon1;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Person(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) {
|
||||
source = JSON.parse(source);
|
||||
}
|
||||
|
||||
this.name = source['name'];
|
||||
this.parent = Person.createFrom(source['parent']);
|
||||
this.details = anon1.createFrom(source['details']);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class anon1 {
|
||||
age: number;
|
||||
address: anon2;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new anon1(source);
|
||||
}
|
||||
export class Person {
|
||||
name: string;
|
||||
parent: Person;
|
||||
details: anon1;
|
||||
|
||||
constructor(source: Partial<Person> = {}) {
|
||||
const { name = "", parent = null, details = null } = source;
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) {
|
||||
source = JSON.parse(source);
|
||||
}
|
||||
static createFrom(source: string | object = {}): Person {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new Person(parsedSource as Partial<Person>);
|
||||
}
|
||||
|
||||
this.age = source['age'];
|
||||
this.address = anon2.createFrom(source['address']);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class anon2 {
|
||||
street: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new anon2(source);
|
||||
}
|
||||
export class anon1 {
|
||||
age: number;
|
||||
address: anon2;
|
||||
|
||||
constructor(source: Partial<anon1> = {}) {
|
||||
const { age = 0, address = null } = source;
|
||||
this.age = age;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) {
|
||||
source = JSON.parse(source);
|
||||
}
|
||||
static createFrom(source: string | object = {}): anon1 {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new anon1(parsedSource as Partial<anon1>);
|
||||
}
|
||||
|
||||
this.street = source['street'];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class anon2 {
|
||||
street: string;
|
||||
|
||||
constructor(source: Partial<anon2> = {}) {
|
||||
const { street = "" } = source;
|
||||
this.street = street;
|
||||
}
|
||||
|
||||
static createFrom(source: string | object = {}): anon2 {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new anon2(parsedSource as Partial<anon2>);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -2,17 +2,14 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.main = {
|
||||
GreetService: {
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet someone
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -2,17 +2,14 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.main = {
|
||||
GreetService: {
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet someone
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -6,15 +6,12 @@
|
||||
* @typedef {import('./models').main.Person} mainPerson
|
||||
*/
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.main = {
|
||||
GreetService: {
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet does XYZ
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
@ -22,9 +19,9 @@ window.go.main = {
|
||||
/**
|
||||
* GreetService.NewPerson
|
||||
* NewPerson creates a new person
|
||||
* @param name {string}
|
||||
* @param name {string}
|
||||
* @returns {Promise<mainPerson>}
|
||||
**/
|
||||
NewPerson: function(name) { return wails.CallByID(1661412647, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -6,17 +6,14 @@
|
||||
* @typedef {import('./models').services.Address} servicesAddress
|
||||
*/
|
||||
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.services = {
|
||||
OtherService: {
|
||||
export const OtherService = {
|
||||
|
||||
/**
|
||||
* OtherService.Yay
|
||||
*
|
||||
*
|
||||
*
|
||||
* @returns {Promise<servicesAddress>}
|
||||
**/
|
||||
Yay: function() { return wails.CallByID(302702907, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -3,49 +3,46 @@
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export namespace main {
|
||||
|
||||
export class Person {
|
||||
name: string;
|
||||
address: services.Address;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Person(source);
|
||||
}
|
||||
|
||||
export class Person {
|
||||
name: string;
|
||||
address: services.Address;
|
||||
|
||||
constructor(source: Partial<Person> = {}) {
|
||||
const { name = "", address = null } = source;
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) {
|
||||
source = JSON.parse(source);
|
||||
}
|
||||
static createFrom(source: string | object = {}): Person {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new Person(parsedSource as Partial<Person>);
|
||||
}
|
||||
|
||||
this.name = source['name'];
|
||||
this.address = services.Address.createFrom(source['address']);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export namespace services {
|
||||
|
||||
export class Address {
|
||||
street: string;
|
||||
state: string;
|
||||
country: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Address(source);
|
||||
}
|
||||
|
||||
export class Address {
|
||||
street: string;
|
||||
state: string;
|
||||
country: string;
|
||||
|
||||
constructor(source: Partial<Address> = {}) {
|
||||
const { street = "", state = "", country = "" } = source;
|
||||
this.street = street;
|
||||
this.state = state;
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) {
|
||||
source = JSON.parse(source);
|
||||
}
|
||||
static createFrom(source: string | object = {}): Address {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new Address(parsedSource as Partial<Address>);
|
||||
}
|
||||
|
||||
this.street = source['street'];
|
||||
this.state = source['state'];
|
||||
this.country = source['country'];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -412,8 +412,8 @@ tasks:
|
||||
dev:
|
||||
summary: Runs the application in development mode
|
||||
preconditions:
|
||||
- sh: wails3 tool checkport -u {{ "\"$WAILS_DEVSERVER_URL\"" }}
|
||||
msg: "Looks like the devserver isn't available. Please ensure you have run `wails3 task dev:frontend` in another terminal."
|
||||
- sh: wails3 tool checkport -u {{ "\"$FRONTEND_DEVSERVER_URL\"" }}
|
||||
msg: 'Cannot connect to frontend dev server ({{ "$FRONTEND_DEVSERVER_URL" }}). Please ensure you have run `wails3 dev:frontend` in another terminal.'
|
||||
cmds:
|
||||
- wails3 tool watcher -path {{ "{{.PATH}}" }} -preexec {{ "\"{{.PREEXEC}}\""}} {{ "{{.PREWAIT}}" }} -postexec {{ "\"{{.POSTEXEC}}\"" }} -ignorefiles {{ "{{.IGNOREFILES}}" }} -ignoredirs {{ "{{.IGNOREDIRS}}" }} -ignoreextensions {{ "{{.IGNOREEXTENSIONS}}" }} -exec {{ "\"{{.EXEC}}\"" }} -debounce {{ "{{.DEBOUNCE}}" }}
|
||||
vars:
|
||||
@ -423,12 +423,12 @@ tasks:
|
||||
EXEC: 'wails3 task run'
|
||||
POSTEXEC: ''
|
||||
IGNOREFILES: 'Taskfile.yml'
|
||||
IGNOREDIRS: '.git,frontend'
|
||||
IGNOREDIRS: '.git,frontend,.idea,.vscode,bin'
|
||||
IGNOREEXTENSIONS: '.exe,.syso'
|
||||
DEBOUNCE: 1000
|
||||
env:
|
||||
# This is the default vite dev server port
|
||||
WAILS_DEVSERVER_URL: 'http://localhost:5173'
|
||||
FRONTEND_DEVSERVER_URL: 'http://localhost:5173'
|
||||
|
||||
dev:reload:
|
||||
summary: Reloads the application
|
||||
|
@ -1,44 +0,0 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/wailsapp/wails/v3/internal/flags"
|
||||
)
|
||||
|
||||
func TestInstall(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
options *flags.Init
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "should install template",
|
||||
options: &flags.Init{
|
||||
ProjectName: "test",
|
||||
TemplateName: "svelte",
|
||||
Quiet: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
// Remove test directory if it exists
|
||||
if _, err := os.Stat(tt.options.ProjectName); err == nil {
|
||||
_ = os.RemoveAll(tt.options.ProjectName)
|
||||
}
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := Install(tt.options); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Install() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
path, _ := os.Getwd()
|
||||
_ = os.RemoveAll(filepath.Join(path, "..", tt.options.ProjectName))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user