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 }