171 lines
3.6 KiB
Go
171 lines
3.6 KiB
Go
package builtin
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
type typeCollector struct {
|
|
mu sync.Mutex
|
|
types map[string]string
|
|
paramTypes map[string]bool
|
|
}
|
|
|
|
func newTypeCollector() *typeCollector {
|
|
return &typeCollector{
|
|
types: make(map[string]string),
|
|
paramTypes: make(map[string]bool),
|
|
}
|
|
}
|
|
|
|
func (tc *typeCollector) collectTypes(argsType reflect.Type, fnType reflect.Type) []string {
|
|
tc.mu.Lock()
|
|
defer tc.mu.Unlock()
|
|
tc.types = make(map[string]string)
|
|
tc.paramTypes = make(map[string]bool)
|
|
|
|
var result []string
|
|
|
|
tc.collectStruct(argsType, argsType.Name())
|
|
|
|
for i := 0; i < argsType.NumField(); i++ {
|
|
field := argsType.Field(i)
|
|
if field.Type.Kind() == reflect.Pointer || strings.Contains(field.Tag.Get("json"), ",omitempty") {
|
|
tc.collectParamType(field.Type)
|
|
}
|
|
}
|
|
|
|
if fnType.Kind() == reflect.Func && fnType.NumOut() > 0 {
|
|
lastIndex := fnType.NumOut() - 1
|
|
lastType := fnType.Out(lastIndex)
|
|
|
|
if lastType.Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
|
if fnType.NumOut() > 1 {
|
|
tc.collectType(fnType.Out(0))
|
|
}
|
|
} else {
|
|
tc.collectType(lastType)
|
|
}
|
|
}
|
|
|
|
for _, t := range tc.types {
|
|
result = append(result, t)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (tc *typeCollector) collectParamType(t reflect.Type) {
|
|
if t.Kind() == reflect.Pointer {
|
|
tc.collectParamType(t.Elem())
|
|
return
|
|
}
|
|
|
|
if t.Kind() == reflect.Struct && t.Name() != "" {
|
|
tc.paramTypes[t.Name()+" | null"] = true
|
|
}
|
|
}
|
|
|
|
func (tc *typeCollector) getParamTypes() map[string]bool {
|
|
return tc.paramTypes
|
|
}
|
|
|
|
func (tc *typeCollector) collectType(t reflect.Type) {
|
|
if t.Kind() == reflect.Pointer {
|
|
tc.collectType(t.Elem())
|
|
return
|
|
}
|
|
|
|
if t.Kind() == reflect.Struct {
|
|
name := t.Name()
|
|
if _, exists := tc.types[name]; !exists {
|
|
tc.collectStruct(t, name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (tc *typeCollector) collectStruct(t reflect.Type, name string) {
|
|
if t.Kind() != reflect.Struct {
|
|
return
|
|
}
|
|
|
|
var fields []string
|
|
for i := 0; i < t.NumField(); i++ {
|
|
field := t.Field(i)
|
|
|
|
if field.Anonymous || !field.IsExported() {
|
|
continue
|
|
}
|
|
|
|
fieldName := getFieldName(field)
|
|
|
|
var tsType string
|
|
var isOptional bool
|
|
isPointer := field.Type.Kind() == reflect.Pointer
|
|
|
|
if isPointer {
|
|
isOptional = true
|
|
tsType = goTypeToTSType(field.Type, false)
|
|
} else {
|
|
isOptional = strings.Contains(field.Tag.Get("json"), ",omitempty")
|
|
tsType = goTypeToTSType(field.Type, false)
|
|
}
|
|
|
|
if isOptional {
|
|
fieldName += "?"
|
|
}
|
|
|
|
fields = append(fields, fmt.Sprintf("%s: %s", fieldName, tsType))
|
|
|
|
tc.collectType(field.Type)
|
|
}
|
|
|
|
tc.types[name] = fmt.Sprintf("interface %s {%s}", name, strings.Join(fields, "; "))
|
|
}
|
|
|
|
func goTypeToTSType(t reflect.Type, isPointer bool) string {
|
|
if t.Kind() == reflect.Pointer {
|
|
return goTypeToTSType(t.Elem(), true)
|
|
}
|
|
|
|
baseType := ""
|
|
switch t.Kind() {
|
|
case reflect.String:
|
|
baseType = "string"
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
baseType = "number"
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
baseType = "number"
|
|
case reflect.Float32, reflect.Float64:
|
|
baseType = "number"
|
|
case reflect.Bool:
|
|
baseType = "boolean"
|
|
case reflect.Interface:
|
|
baseType = "any"
|
|
case reflect.Slice:
|
|
baseType = fmt.Sprintf("%s[]", goTypeToTSType(t.Elem(), false))
|
|
case reflect.Map:
|
|
if t.Key().Kind() == reflect.String && t.Elem().Kind() == reflect.Interface {
|
|
baseType = "Record<string, any>"
|
|
} else {
|
|
baseType = "Record<string, any>"
|
|
}
|
|
case reflect.Struct:
|
|
name := t.Name()
|
|
if name == "" {
|
|
baseType = "{}"
|
|
} else {
|
|
baseType = name
|
|
}
|
|
default:
|
|
baseType = "any"
|
|
}
|
|
|
|
if isPointer {
|
|
baseType += " | null"
|
|
}
|
|
return baseType
|
|
}
|