Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for interface generation and enums #3047

Merged
merged 14 commits into from
Nov 25, 2023
Merged
11 changes: 8 additions & 3 deletions v2/cmd/wails/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,15 @@ func generateModule(f *flags.GenerateModule) error {
return err
}

if projectConfig.Bindings.TsGeneration.OutputType == "" {
projectConfig.Bindings.TsGeneration.OutputType = "classes"
}

_, err = bindings.GenerateBindings(bindings.Options{
Tags: buildTags,
TsPrefix: projectConfig.Bindings.TsGeneration.Prefix,
TsSuffix: projectConfig.Bindings.TsGeneration.Suffix,
Tags: buildTags,
TsPrefix: projectConfig.Bindings.TsGeneration.Prefix,
TsSuffix: projectConfig.Bindings.TsGeneration.Suffix,
TsOutputType: projectConfig.Bindings.TsGeneration.OutputType,
})
if err != nil {
return err
Expand Down
12 changes: 11 additions & 1 deletion v2/internal/app/app_bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func (a *App) Run() error {

var tsPrefixFlag *string
var tsPostfixFlag *string
var tsOutputTypeFlag *string

tsPrefix := os.Getenv("tsprefix")
if tsPrefix == "" {
Expand All @@ -42,18 +43,27 @@ func (a *App) Run() error {
tsPostfixFlag = bindingFlags.String("tssuffix", "", "Suffix for generated typescript entities")
}

tsOutputType := os.Getenv("tsoutputtype")
if tsOutputType == "" {
tsOutputTypeFlag = bindingFlags.String("tsoutputtype", "", "Output type for generated typescript entities (classes|interfaces)")
}

_ = bindingFlags.Parse(os.Args[1:])
if tsPrefixFlag != nil {
tsPrefix = *tsPrefixFlag
}
if tsPostfixFlag != nil {
tsSuffix = *tsPostfixFlag
}
if tsOutputTypeFlag != nil {
tsOutputType = *tsOutputTypeFlag
}

appBindings := binding.NewBindings(a.logger, a.options.Bind, bindingExemptions, IsObfuscated())
appBindings := binding.NewBindings(a.logger, a.options.Bind, bindingExemptions, IsObfuscated(), a.options.EnumBind)

appBindings.SetTsPrefix(tsPrefix)
appBindings.SetTsSuffix(tsSuffix)
appBindings.SetOutputType(tsOutputType)

err := generateBindings(appBindings)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion v2/internal/app/app_dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
appoptions.OnDomReady,
appoptions.OnBeforeClose,
}
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, false)
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, false, appoptions.EnumBind)

eventHandler := runtime.NewEvents(myLogger)
ctx = context.WithValue(ctx, "events", eventHandler)
Expand Down
2 changes: 1 addition & 1 deletion v2/internal/app/app_production.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
appoptions.OnDomReady,
appoptions.OnBeforeClose,
}
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, IsObfuscated())
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, IsObfuscated(), appoptions.EnumBind)
eventHandler := runtime.NewEvents(myLogger)
ctx = context.WithValue(ctx, "events", eventHandler)
// Attach logger to context
Expand Down
95 changes: 94 additions & 1 deletion v2/internal/binding/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,20 @@ type Bindings struct {
exemptions slicer.StringSlicer

structsToGenerateTS map[string]map[string]interface{}
enumsToGenerateTS map[string]map[string]interface{}
tsPrefix string
tsSuffix string
tsInterface bool
obfuscate bool
}

// NewBindings returns a new Bindings object
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}, obfuscate bool) *Bindings {
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}, obfuscate bool, enumsToBind []interface{}) *Bindings {
result := &Bindings{
db: newDB(),
logger: logger.CustomLogger("Bindings"),
structsToGenerateTS: make(map[string]map[string]interface{}),
enumsToGenerateTS: make(map[string]map[string]interface{}),
obfuscate: obfuscate,
}

Expand All @@ -47,6 +50,10 @@ func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exem
result.exemptions.Add(name)
}

for _, enum := range enumsToBind {
result.AddEnumToGenerateTS(enum)
}

// Add the structs to bind
for _, ptr := range structPointersToBind {
err := result.Add(ptr)
Expand Down Expand Up @@ -89,13 +96,15 @@ func (b *Bindings) ToJSON() (string, error) {
func (b *Bindings) GenerateModels() ([]byte, error) {
models := map[string]string{}
var seen slicer.StringSlicer
var seenEnumsPackages slicer.StringSlicer
allStructNames := b.getAllStructNames()
allStructNames.Sort()
for packageName, structsToGenerate := range b.structsToGenerateTS {
thisPackageCode := ""
w := typescriptify.New()
w.WithPrefix(b.tsPrefix)
w.WithSuffix(b.tsSuffix)
w.WithInterface(b.tsInterface)
w.Namespace = packageName
w.WithBackupDir("")
w.KnownStructs = allStructNames
Expand All @@ -113,6 +122,20 @@ func (b *Bindings) GenerateModels() ([]byte, error) {
structInterface := structsToGenerate[structName]
w.Add(structInterface)
}

// if we have enums for this package, add them as well
var enums, enumsExist = b.enumsToGenerateTS[packageName]
if enumsExist {
for enumName, enum := range enums {
fqemumname := packageName + "." + enumName
if seen.Contains(fqemumname) {
continue
}
w.AddEnum(enum)
}
seenEnumsPackages.Add(packageName)
}

str, err := w.Convert(nil)
if err != nil {
return nil, err
Expand All @@ -122,6 +145,35 @@ func (b *Bindings) GenerateModels() ([]byte, error) {
models[packageName] = thisPackageCode
}

// Add outstanding enums to the models that were not in packages with structs
for packageName, enumsToGenerate := range b.enumsToGenerateTS {
if seenEnumsPackages.Contains(packageName) {
continue
}

thisPackageCode := ""
w := typescriptify.New()
w.WithPrefix(b.tsPrefix)
w.WithSuffix(b.tsSuffix)
w.WithInterface(b.tsInterface)
w.Namespace = packageName
w.WithBackupDir("")

for enumName, enum := range enumsToGenerate {
fqemumname := packageName + "." + enumName
if seen.Contains(fqemumname) {
continue
}
w.AddEnum(enum)
}
str, err := w.Convert(nil)
if err != nil {
return nil, err
}
thisPackageCode += str
models[packageName] = thisPackageCode
}

// Sort the package names first to make the output deterministic
sortedPackageNames := make([]string, 0)
for packageName := range models {
Expand Down Expand Up @@ -165,6 +217,40 @@ func (b *Bindings) WriteModels(modelsDir string) error {
return nil
}

func (b *Bindings) AddEnumToGenerateTS(e interface{}) {
enumType := reflect.TypeOf(e)

var packageName string
var enumName string
// enums should be represented as array of all possible values
if hasElements(enumType) {
enum := enumType.Elem()
println(enum.Kind())
APshenkin marked this conversation as resolved.
Show resolved Hide resolved
// simple enum represented by struct with Value/TSName fields
if enum.Kind() == reflect.Struct {
_, tsNamePresented := enum.FieldByName("TSName")
enumT, valuePresented := enum.FieldByName("Value")
if tsNamePresented && valuePresented {
packageName = getPackageName(enumT.Type.String())
enumName = enumT.Type.Name()
} else {
return
}
// otherwise expecting implementation with TSName() https://github.com/tkrajina/typescriptify-golang-structs#enums-with-tsname
} else {
packageName = getPackageName(enumType.Elem().String())
enumName = enumType.Elem().Name()
}
if b.enumsToGenerateTS[packageName] == nil {
b.enumsToGenerateTS[packageName] = make(map[string]interface{})
}
if b.enumsToGenerateTS[packageName][enumName] != nil {
return
}
b.enumsToGenerateTS[packageName][enumName] = e
}
}

func (b *Bindings) AddStructToGenerateTS(packageName string, structName string, s interface{}) {
if b.structsToGenerateTS[packageName] == nil {
b.structsToGenerateTS[packageName] = make(map[string]interface{})
Expand Down Expand Up @@ -233,6 +319,13 @@ func (b *Bindings) SetTsSuffix(postfix string) *Bindings {
return b
}

func (b *Bindings) SetOutputType(outputType string) *Bindings {
if outputType == "interfaces" {
b.tsInterface = true
}
return b
}

func (b *Bindings) getAllStructNames() *slicer.StringSlicer {
var result slicer.StringSlicer
for packageName, structsToGenerate := range b.structsToGenerateTS {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestConflictingPackageName(t *testing.T) {

// setup
testLogger := &logger.Logger{}
b := binding.NewBindings(testLogger, []interface{}{&HandlerTest{}}, []interface{}{}, false)
b := binding.NewBindings(testLogger, []interface{}{&HandlerTest{}}, []interface{}{}, false, []interface{}{})

// then
err := b.GenerateGoBindings(generationDir)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestPromises(t *testing.T) {

// setup
testLogger := &logger.Logger{}
b := binding.NewBindings(testLogger, []interface{}{&PromisesTest{}}, []interface{}{}, false)
b := binding.NewBindings(testLogger, []interface{}{&PromisesTest{}}, []interface{}{}, false, []interface{}{})

// then
err := b.GenerateGoBindings(generationDir)
Expand Down
2 changes: 1 addition & 1 deletion v2/internal/binding/binding_test/binding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func TestBindings_GenerateModels(t *testing.T) {
testLogger := &logger.Logger{}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := binding.NewBindings(testLogger, tt.structs, tt.exemptions, false)
b := binding.NewBindings(testLogger, tt.structs, tt.exemptions, false, []interface{}{})
for _, s := range tt.structs {
err := b.Add(s)
require.NoError(t, err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func TestAliases(t *testing.T) {

// setup
testLogger := &logger.Logger{}
b := binding.NewBindings(testLogger, []interface{}{&AliasTest{}}, []interface{}{}, false)
b := binding.NewBindings(testLogger, []interface{}{&AliasTest{}}, []interface{}{}, false, []interface{}{})

// then
err := b.GenerateGoBindings(generationDir)
Expand Down
2 changes: 1 addition & 1 deletion v2/internal/binding/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type B struct {

func TestNestedStruct(t *testing.T) {
bind := &BindForTest{}
testBindings := NewBindings(logger.New(nil), []interface{}{bind}, []interface{}{}, false)
testBindings := NewBindings(logger.New(nil), []interface{}{bind}, []interface{}{}, false, []interface{}{})

namesStrSlicer := testBindings.getAllStructNames()
names := []string{}
Expand Down
5 changes: 3 additions & 2 deletions v2/internal/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,9 @@ type Bindings struct {
}

type TsGeneration struct {
Prefix string `json:"prefix"`
Suffix string `json:"suffix"`
Prefix string `json:"prefix"`
Suffix string `json:"suffix"`
OutputType string `json:"outputType"`
}

// Parse the given JSON data into a Project struct
Expand Down
2 changes: 2 additions & 0 deletions v2/pkg/commands/bindings/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Options struct {
GoModTidy bool
TsPrefix string
TsSuffix string
TsOutputType string
}

// GenerateBindings generates bindings for the Wails project in the given ProjectDirectory.
Expand Down Expand Up @@ -66,6 +67,7 @@ func GenerateBindings(options Options) (string, error) {
env := os.Environ()
env = shell.SetEnv(env, "tsprefix", options.TsPrefix)
env = shell.SetEnv(env, "tssuffix", options.TsSuffix)
env = shell.SetEnv(env, "tsoutputtype", options.TsOutputType)

stdout, stderr, err = shell.RunCommandWithEnv(env, workingDirectory, filename)
if err != nil {
Expand Down
13 changes: 9 additions & 4 deletions v2/pkg/commands/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,17 @@ func GenerateBindings(buildOptions *Options) error {
printBulletPoint("Generating bindings: ")
}

if buildOptions.ProjectData.Bindings.TsGeneration.OutputType == "" {
buildOptions.ProjectData.Bindings.TsGeneration.OutputType = "classes"
}

// Generate Bindings
output, err := bindings.GenerateBindings(bindings.Options{
Tags: buildOptions.UserTags,
GoModTidy: !buildOptions.SkipModTidy,
TsPrefix: buildOptions.ProjectData.Bindings.TsGeneration.Prefix,
TsSuffix: buildOptions.ProjectData.Bindings.TsGeneration.Suffix,
Tags: buildOptions.UserTags,
GoModTidy: !buildOptions.SkipModTidy,
TsPrefix: buildOptions.ProjectData.Bindings.TsGeneration.Prefix,
TsSuffix: buildOptions.ProjectData.Bindings.TsGeneration.Suffix,
TsOutputType: buildOptions.ProjectData.Bindings.TsGeneration.OutputType,
})
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions v2/pkg/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type App struct {
OnShutdown func(ctx context.Context) `json:"-"`
OnBeforeClose func(ctx context.Context) (prevent bool) `json:"-"`
Bind []interface{}
EnumBind []interface{}
WindowStartState WindowStartState

// ErrorFormatter overrides the formatting of errors returned by backend methods
Expand Down
Loading
Loading