2020-09-15 19:52:54 -05:00
package binding
import (
"fmt"
"reflect"
2021-01-03 22:05:40 +11:00
"runtime"
2022-04-04 21:01:00 +10:00
"strings"
2020-09-15 19:52:54 -05:00
)
// isStructPtr returns true if the value given is a
// pointer to a struct
func isStructPtr ( value interface {}) bool {
return reflect . ValueOf ( value ). Kind () == reflect . Ptr &&
reflect . ValueOf ( value ). Elem (). Kind () == reflect . Struct
}
2021-01-03 22:05:40 +11:00
// isFunction returns true if the given value is a function
func isFunction ( value interface {}) bool {
return reflect . ValueOf ( value ). Kind () == reflect . Func
}
2024-04-10 06:19:06 +08:00
// isStruct returns true if the value given is a struct
2020-09-15 19:52:54 -05:00
func isStruct ( value interface {}) bool {
return reflect . ValueOf ( value ). Kind () == reflect . Struct
}
2021-01-27 21:12:17 +11:00
func ( b * Bindings ) getMethods ( value interface {}) ([] * BoundMethod , error ) {
2020-09-15 19:52:54 -05:00
// Create result placeholder
var result [] * BoundMethod
// Check type
if ! isStructPtr ( value ) {
if isStruct ( value ) {
name := reflect . ValueOf ( value ). Type (). Name ()
return nil , fmt . Errorf ( "%s is a struct, not a pointer to a struct" , name )
}
2021-01-03 22:05:40 +11:00
if isFunction ( value ) {
name := runtime . FuncForPC ( reflect . ValueOf ( value ). Pointer ()). Name ()
return nil , fmt . Errorf ( "%s is a function, not a pointer to a struct. Wails v2 has deprecated the binding of functions. Please wrap your functions up in a struct and bind a pointer to that struct." , name )
}
return nil , fmt . Errorf ( "not a pointer to a struct." )
2020-09-15 19:52:54 -05:00
}
// Process Struct
structType := reflect . TypeOf ( value )
structValue := reflect . ValueOf ( value )
2022-04-04 21:01:00 +10:00
structTypeString := structType . String ()
baseName := structTypeString [ 1 :]
2020-09-15 19:52:54 -05:00
// Process Methods
for i := 0 ; i < structType . NumMethod (); i ++ {
methodDef := structType . Method ( i )
methodName := methodDef . Name
fullMethodName := baseName + "." + methodName
method := structValue . MethodByName ( methodName )
2021-01-27 21:12:17 +11:00
methodReflectName := runtime . FuncForPC ( methodDef . Func . Pointer ()). Name ()
if b . exemptions . Contains ( methodReflectName ) {
continue
}
2020-09-15 19:52:54 -05:00
// Create new method
boundMethod := & BoundMethod {
Name : fullMethodName ,
Inputs : nil ,
Outputs : nil ,
Comments : "" ,
Method : method ,
}
// Iterate inputs
methodType := method . Type ()
inputParamCount := methodType . NumIn ()
var inputs [] * Parameter
for inputIndex := 0 ; inputIndex < inputParamCount ; inputIndex ++ {
input := methodType . In ( inputIndex )
thisParam := newParameter ( "" , input )
2021-08-27 21:11:03 +10:00
2021-11-18 17:54:09 +11:00
thisInput := input
if thisInput . Kind () == reflect . Slice {
thisInput = thisInput . Elem ()
}
2021-08-27 21:11:03 +10:00
// Process struct pointer params
2021-11-18 17:54:09 +11:00
if thisInput . Kind () == reflect . Ptr {
if thisInput . Elem (). Kind () == reflect . Struct {
typ := thisInput . Elem ()
2021-08-27 21:11:03 +10:00
a := reflect . New ( typ )
s := reflect . Indirect ( a ). Interface ()
2022-04-04 21:01:00 +10:00
name := typ . Name ()
2022-04-05 08:26:54 +10:00
packageName := getPackageName ( thisInput . String ())
2022-04-04 21:01:00 +10:00
b . AddStructToGenerateTS ( packageName , name , s )
2021-08-27 21:11:03 +10:00
}
}
// Process struct params
2021-11-18 17:54:09 +11:00
if thisInput . Kind () == reflect . Struct {
a := reflect . New ( thisInput )
2021-08-27 21:11:03 +10:00
s := reflect . Indirect ( a ). Interface ()
2022-04-04 21:01:00 +10:00
name := thisInput . Name ()
2022-04-05 08:26:54 +10:00
packageName := getPackageName ( thisInput . String ())
2022-04-04 21:01:00 +10:00
b . AddStructToGenerateTS ( packageName , name , s )
2021-08-27 21:11:03 +10:00
}
2020-09-15 19:52:54 -05:00
inputs = append ( inputs , thisParam )
}
boundMethod . Inputs = inputs
// Iterate outputs
2020-11-15 09:25:38 +11:00
// TODO: Determine what to do about limiting return types
// especially around errors.
2020-09-15 19:52:54 -05:00
outputParamCount := methodType . NumOut ()
var outputs [] * Parameter
for outputIndex := 0 ; outputIndex < outputParamCount ; outputIndex ++ {
output := methodType . Out ( outputIndex )
thisParam := newParameter ( "" , output )
2021-11-18 17:54:09 +11:00
thisOutput := output
if thisOutput . Kind () == reflect . Slice {
thisOutput = thisOutput . Elem ()
}
// Process struct pointer params
if thisOutput . Kind () == reflect . Ptr {
if thisOutput . Elem (). Kind () == reflect . Struct {
typ := thisOutput . Elem ()
a := reflect . New ( typ )
s := reflect . Indirect ( a ). Interface ()
2022-04-04 21:01:00 +10:00
name := typ . Name ()
2022-04-05 08:26:54 +10:00
packageName := getPackageName ( thisOutput . String ())
2022-04-04 21:01:00 +10:00
b . AddStructToGenerateTS ( packageName , name , s )
2021-11-18 17:54:09 +11:00
}
}
// Process struct params
if thisOutput . Kind () == reflect . Struct {
a := reflect . New ( thisOutput )
s := reflect . Indirect ( a ). Interface ()
2022-04-04 21:01:00 +10:00
name := thisOutput . Name ()
2022-04-05 08:26:54 +10:00
packageName := getPackageName ( thisOutput . String ())
2022-04-04 21:01:00 +10:00
b . AddStructToGenerateTS ( packageName , name , s )
2021-11-18 17:54:09 +11:00
}
2020-09-15 19:52:54 -05:00
outputs = append ( outputs , thisParam )
}
boundMethod . Outputs = outputs
// Save method in result
result = append ( result , boundMethod )
}
return result , nil
}
2022-04-05 08:26:54 +10:00
func getPackageName ( in string ) string {
result := strings . Split ( in , "." )[ 0 ]
result = strings . ReplaceAll ( result , "[]" , "" )
result = strings . ReplaceAll ( result , "*" , "" )
return result
}
2022-10-01 05:49:51 +00:00
2022-11-24 11:33:58 +01:00
func getSplitReturn ( in string ) ( string , string ) {
result := strings . Split ( in , "." )
return result [ 0 ], result [ 1 ]
}
2022-10-01 05:49:51 +00:00
func hasElements ( typ reflect . Type ) bool {
kind := typ . Kind ()
return kind == reflect . Ptr || kind == reflect . Array || kind == reflect . Slice || kind == reflect . Map
}