github.com/AlpineAIO/wails/v2@v2.0.0-beta.32.0.20240505041856-1047a8fa5fef/internal/binding/reflect.go (about)

     1  package binding
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"runtime"
     7  	"strings"
     8  )
     9  
    10  // isStructPtr returns true if the value given is a
    11  // pointer to a struct
    12  func isStructPtr(value interface{}) bool {
    13  	return reflect.ValueOf(value).Kind() == reflect.Ptr &&
    14  		reflect.ValueOf(value).Elem().Kind() == reflect.Struct
    15  }
    16  
    17  // isFunction returns true if the given value is a function
    18  func isFunction(value interface{}) bool {
    19  	return reflect.ValueOf(value).Kind() == reflect.Func
    20  }
    21  
    22  // isStructPtr returns true if the value given is a struct
    23  func isStruct(value interface{}) bool {
    24  	return reflect.ValueOf(value).Kind() == reflect.Struct
    25  }
    26  
    27  func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
    28  	// Create result placeholder
    29  	var result []*BoundMethod
    30  
    31  	// Check type
    32  	if !isStructPtr(value) {
    33  
    34  		if isStruct(value) {
    35  			name := reflect.ValueOf(value).Type().Name()
    36  			return nil, fmt.Errorf("%s is a struct, not a pointer to a struct", name)
    37  		}
    38  
    39  		if isFunction(value) {
    40  			name := runtime.FuncForPC(reflect.ValueOf(value).Pointer()).Name()
    41  			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)
    42  		}
    43  
    44  		return nil, fmt.Errorf("not a pointer to a struct.")
    45  	}
    46  
    47  	// Process Struct
    48  	structType := reflect.TypeOf(value)
    49  	structValue := reflect.ValueOf(value)
    50  	structTypeString := structType.String()
    51  	baseName := structTypeString[1:]
    52  
    53  	// Process Methods
    54  	for i := 0; i < structType.NumMethod(); i++ {
    55  		methodDef := structType.Method(i)
    56  		methodName := methodDef.Name
    57  		fullMethodName := baseName + "." + methodName
    58  		method := structValue.MethodByName(methodName)
    59  
    60  		methodReflectName := runtime.FuncForPC(methodDef.Func.Pointer()).Name()
    61  		if b.exemptions.Contains(methodReflectName) {
    62  			continue
    63  		}
    64  
    65  		// Create new method
    66  		boundMethod := &BoundMethod{
    67  			Name:     fullMethodName,
    68  			Inputs:   nil,
    69  			Outputs:  nil,
    70  			Comments: "",
    71  			Method:   method,
    72  		}
    73  
    74  		// Iterate inputs
    75  		methodType := method.Type()
    76  		inputParamCount := methodType.NumIn()
    77  		var inputs []*Parameter
    78  		for inputIndex := 0; inputIndex < inputParamCount; inputIndex++ {
    79  			input := methodType.In(inputIndex)
    80  			thisParam := newParameter("", input)
    81  
    82  			thisInput := input
    83  
    84  			if thisInput.Kind() == reflect.Slice {
    85  				thisInput = thisInput.Elem()
    86  			}
    87  
    88  			// Process struct pointer params
    89  			if thisInput.Kind() == reflect.Ptr {
    90  				if thisInput.Elem().Kind() == reflect.Struct {
    91  					typ := thisInput.Elem()
    92  					a := reflect.New(typ)
    93  					s := reflect.Indirect(a).Interface()
    94  					name := typ.Name()
    95  					packageName := getPackageName(thisInput.String())
    96  					b.AddStructToGenerateTS(packageName, name, s)
    97  				}
    98  			}
    99  
   100  			// Process struct params
   101  			if thisInput.Kind() == reflect.Struct {
   102  				a := reflect.New(thisInput)
   103  				s := reflect.Indirect(a).Interface()
   104  				name := thisInput.Name()
   105  				packageName := getPackageName(thisInput.String())
   106  				b.AddStructToGenerateTS(packageName, name, s)
   107  			}
   108  
   109  			inputs = append(inputs, thisParam)
   110  		}
   111  
   112  		boundMethod.Inputs = inputs
   113  
   114  		// Iterate outputs
   115  		// TODO: Determine what to do about limiting return types
   116  		//       especially around errors.
   117  		outputParamCount := methodType.NumOut()
   118  		var outputs []*Parameter
   119  		for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ {
   120  			output := methodType.Out(outputIndex)
   121  			thisParam := newParameter("", output)
   122  
   123  			thisOutput := output
   124  
   125  			if thisOutput.Kind() == reflect.Slice {
   126  				thisOutput = thisOutput.Elem()
   127  			}
   128  
   129  			// Process struct pointer params
   130  			if thisOutput.Kind() == reflect.Ptr {
   131  				if thisOutput.Elem().Kind() == reflect.Struct {
   132  					typ := thisOutput.Elem()
   133  					a := reflect.New(typ)
   134  					s := reflect.Indirect(a).Interface()
   135  					name := typ.Name()
   136  					packageName := getPackageName(thisOutput.String())
   137  					b.AddStructToGenerateTS(packageName, name, s)
   138  				}
   139  			}
   140  
   141  			// Process struct params
   142  			if thisOutput.Kind() == reflect.Struct {
   143  				a := reflect.New(thisOutput)
   144  				s := reflect.Indirect(a).Interface()
   145  				name := thisOutput.Name()
   146  				packageName := getPackageName(thisOutput.String())
   147  				b.AddStructToGenerateTS(packageName, name, s)
   148  			}
   149  
   150  			outputs = append(outputs, thisParam)
   151  		}
   152  		boundMethod.Outputs = outputs
   153  
   154  		// Save method in result
   155  		result = append(result, boundMethod)
   156  
   157  	}
   158  	return result, nil
   159  }
   160  
   161  func getPackageName(in string) string {
   162  	result := strings.Split(in, ".")[0]
   163  	result = strings.ReplaceAll(result, "[]", "")
   164  	result = strings.ReplaceAll(result, "*", "")
   165  	return result
   166  }
   167  
   168  func getSplitReturn(in string) (string, string) {
   169  	result := strings.Split(in, ".")
   170  	return result[0], result[1]
   171  }
   172  
   173  func hasElements(typ reflect.Type) bool {
   174  	kind := typ.Kind()
   175  	return kind == reflect.Ptr || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map
   176  }