update BlockService to use the new type union feature of tsgen. strongly type the arguments to BlockService.SendCommand

This commit is contained in:
sawka 2024-06-12 13:47:13 -07:00
parent 605b9ea048
commit 083e00227e
13 changed files with 195 additions and 101 deletions
cmd/generate
frontend
pkg
blockcontroller
service
tsgen

View File

@ -14,14 +14,13 @@ import (
"github.com/wavetermdev/thenextwave/pkg/util/utilfn"
)
func generateTypesFile() error {
func generateTypesFile(tsTypesMap map[reflect.Type]string) error {
fd, err := os.Create("frontend/types/gotypes.d.ts")
if err != nil {
return err
}
defer fd.Close()
fmt.Fprintf(os.Stderr, "generating types file to %s\n", fd.Name())
tsTypesMap := make(map[reflect.Type]string)
tsgen.GenerateWaveObjTypes(tsTypesMap)
err = tsgen.GenerateServiceTypes(tsTypesMap)
if err != nil {
@ -37,8 +36,8 @@ func generateTypesFile() error {
keys = append(keys, key)
}
sort.Slice(keys, func(i, j int) bool {
iname, _ := tsgen.TypeToTSType(keys[i])
jname, _ := tsgen.TypeToTSType(keys[j])
iname, _ := tsgen.TypeToTSType(keys[i], tsTypesMap)
jname, _ := tsgen.TypeToTSType(keys[j], tsTypesMap)
return iname < jname
})
for _, key := range keys {
@ -51,7 +50,7 @@ func generateTypesFile() error {
return nil
}
func generateServicesFile() error {
func generateServicesFile(tsTypesMap map[reflect.Type]string) error {
fd, err := os.Create("frontend/app/store/services.ts")
if err != nil {
return err
@ -65,7 +64,7 @@ func generateServicesFile() error {
orderedKeys := utilfn.GetOrderedMapKeys(service.ServiceMap)
for _, serviceName := range orderedKeys {
serviceObj := service.ServiceMap[serviceName]
svcStr := tsgen.GenerateServiceClass(serviceName, serviceObj)
svcStr := tsgen.GenerateServiceClass(serviceName, serviceObj, tsTypesMap)
fmt.Fprint(fd, svcStr)
fmt.Fprint(fd, "\n")
}
@ -78,12 +77,13 @@ func main() {
fmt.Fprintf(os.Stderr, "Error validating service map: %v\n", err)
os.Exit(1)
}
err = generateTypesFile()
tsTypesMap := make(map[reflect.Type]string)
err = generateTypesFile(tsTypesMap)
if err != nil {
fmt.Fprintf(os.Stderr, "Error generating types file: %v\n", err)
os.Exit(1)
}
err = generateServicesFile()
err = generateServicesFile(tsTypesMap)
if err != nil {
fmt.Fprintf(os.Stderr, "Error generating services file: %v\n", err)
os.Exit(1)

View File

@ -49,7 +49,7 @@ const Block = ({ blockId, onClose }: BlockProps) => {
} else if (blockData.view === "plot") {
blockElem = <PlotView />;
} else if (blockData.view === "codeedit") {
blockElem = <CodeEditView />;
blockElem = <CodeEditView text={null} />;
}
return (
<div className="block" ref={blockRef}>

View File

@ -8,7 +8,7 @@ import * as WOS from "./wos";
// blockservice.BlockService (block)
class BlockServiceType {
// send command to block
SendCommand(blockid: string, command: MetaType): Promise<void> {
SendCommand(blockid: string, cmd: BlockCommand): Promise<void> {
return WOS.callBackendService("block", "SendCommand", Array.from(arguments))
}
}

View File

@ -49,7 +49,10 @@ function handleResize(fitAddon: FitAddon, blockId: string, term: Terminal) {
const oldCols = term.cols;
fitAddon.fit();
if (oldRows !== term.rows || oldCols !== term.cols) {
const resizeCommand = { command: "controller:input", termsize: { rows: term.rows, cols: term.cols } };
const resizeCommand: BlockInputCommand = {
command: "controller:input",
termsize: { rows: term.rows, cols: term.cols },
};
services.BlockService.SendCommand(blockId, resizeCommand);
}
}
@ -78,17 +81,13 @@ const TerminalView = ({ blockId }: { blockId: string }) => {
newTerm.loadAddon(newFitAddon);
newTerm.open(connectElemRef.current);
newFitAddon.fit();
// BlockService.SendCommand(blockId, {
// command: "controller:input",
// termsize: { rows: newTerm.rows, cols: newTerm.cols },
// });
services.BlockService.SendCommand(blockId, {
command: "controller:input",
termsize: { rows: newTerm.rows, cols: newTerm.cols },
});
newTerm.onData((data) => {
const b64data = btoa(data);
const inputCmd = { command: "controller:input", blockid: blockId, inputdata64: b64data };
const inputCmd: BlockInputCommand = { command: "controller:input", inputdata64: b64data };
services.BlockService.SendCommand(blockId, inputCmd);
});

View File

@ -14,6 +14,10 @@ declare global {
meta: MetaType;
};
type BlockCommand = {
command: string;
} & ( BlockMessageCommand | BlockInputCommand | BlockSetViewCommand | BlockSetMetaCommand );
// wstore.BlockDef
type BlockDef = {
controller?: string;
@ -22,6 +26,32 @@ declare global {
meta?: MetaType;
};
// blockcontroller.BlockInputCommand
type BlockInputCommand = {
command: "controller:input";
inputdata64?: string;
signame?: string;
termsize?: TermSize;
};
// blockcontroller.BlockMessageCommand
type BlockMessageCommand = {
command: "message";
message: string;
};
// blockcontroller.BlockSetMetaCommand
type BlockSetMetaCommand = {
command: "setmeta";
meta: MetaType;
};
// blockcontroller.BlockSetViewCommand
type BlockSetViewCommand = {
command: "setview";
view: string;
};
// wstore.Client
type Client = WaveObj & {
mainwindowid: string;
@ -62,7 +92,7 @@ declare global {
type MetaType = {[key: string]: any}
// servicemeta.MethodMeta
// tsgenmeta.MethodMeta
type MethodMeta = {
Desc: string;
ArgNames: string[];

View File

@ -9,6 +9,7 @@ import (
"reflect"
"github.com/wavetermdev/thenextwave/pkg/shellexec"
"github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
)
const CommandKey = "command"
@ -21,16 +22,33 @@ const (
)
var CommandToTypeMap = map[string]reflect.Type{
BlockCommand_Message: reflect.TypeOf(MessageCommand{}),
BlockCommand_Input: reflect.TypeOf(InputCommand{}),
BlockCommand_SetView: reflect.TypeOf(SetViewCommand{}),
BlockCommand_SetMeta: reflect.TypeOf(SetMetaCommand{}),
BlockCommand_Message: reflect.TypeOf(BlockMessageCommand{}),
BlockCommand_Input: reflect.TypeOf(BlockInputCommand{}),
BlockCommand_SetView: reflect.TypeOf(BlockSetViewCommand{}),
BlockCommand_SetMeta: reflect.TypeOf(BlockSetMetaCommand{}),
}
func CommandTypeUnionMeta() tsgenmeta.TypeUnionMeta {
return tsgenmeta.TypeUnionMeta{
BaseType: reflect.TypeOf((*BlockCommand)(nil)).Elem(),
TypeFieldName: "command",
Types: []reflect.Type{
reflect.TypeOf(BlockMessageCommand{}),
reflect.TypeOf(BlockInputCommand{}),
reflect.TypeOf(BlockSetViewCommand{}),
reflect.TypeOf(BlockSetMetaCommand{}),
},
}
}
type BlockCommand interface {
GetCommand() string
}
type BlockCommandWrapper struct {
BlockCommand
}
func ParseCmdMap(cmdMap map[string]any) (BlockCommand, error) {
cmdType, ok := cmdMap[CommandKey].(string)
if !ok {
@ -52,40 +70,40 @@ func ParseCmdMap(cmdMap map[string]any) (BlockCommand, error) {
return cmd.(BlockCommand), nil
}
type MessageCommand struct {
Command string `json:"command"`
type BlockMessageCommand struct {
Command string `json:"command" tstype:"\"message\""`
Message string `json:"message"`
}
func (mc *MessageCommand) GetCommand() string {
func (mc *BlockMessageCommand) GetCommand() string {
return BlockCommand_Message
}
type InputCommand struct {
Command string `json:"command"`
InputData64 string `json:"inputdata64"`
type BlockInputCommand struct {
Command string `json:"command" tstype:"\"controller:input\""`
InputData64 string `json:"inputdata64,omitempty"`
SigName string `json:"signame,omitempty"`
TermSize *shellexec.TermSize `json:"termsize,omitempty"`
}
func (ic *InputCommand) GetCommand() string {
func (ic *BlockInputCommand) GetCommand() string {
return BlockCommand_Input
}
type SetViewCommand struct {
Command string `json:"command"`
type BlockSetViewCommand struct {
Command string `json:"command" tstype:"\"setview\""`
View string `json:"view"`
}
func (svc *SetViewCommand) GetCommand() string {
func (svc *BlockSetViewCommand) GetCommand() string {
return BlockCommand_SetView
}
type SetMetaCommand struct {
Command string `json:"command"`
type BlockSetMetaCommand struct {
Command string `json:"command" tstype:"\"setmeta\""`
Meta map[string]any `json:"meta"`
}
func (smc *SetMetaCommand) GetCommand() string {
func (smc *BlockSetMetaCommand) GetCommand() string {
return BlockCommand_SetMeta
}

View File

@ -40,7 +40,7 @@ type BlockController struct {
Status string
ShellProc *shellexec.ShellProc
ShellInputCh chan *InputCommand
ShellInputCh chan *BlockInputCommand
}
func (bc *BlockController) WithLock(f func()) {
@ -149,7 +149,7 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts) error {
bc.ShellProc.Close()
return err
}
shellInputCh := make(chan *InputCommand)
shellInputCh := make(chan *BlockInputCommand)
bc.ShellInputCh = shellInputCh
go func() {
defer func() {
@ -234,7 +234,7 @@ func (bc *BlockController) Run(bdata *wstore.Block) {
for genCmd := range bc.InputCh {
switch cmd := genCmd.(type) {
case *InputCommand:
case *BlockInputCommand:
fmt.Printf("INPUT: %s | %q\n", bc.BlockId, cmd.InputData64)
if bc.ShellInputCh != nil {
bc.ShellInputCh <- cmd
@ -293,10 +293,10 @@ func ProcessStaticCommand(blockId string, cmdGen BlockCommand) error {
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancelFn()
switch cmd := cmdGen.(type) {
case *MessageCommand:
case *BlockMessageCommand:
log.Printf("MESSAGE: %s | %q\n", blockId, cmd.Message)
return nil
case *SetViewCommand:
case *BlockSetViewCommand:
log.Printf("SETVIEW: %s | %q\n", blockId, cmd.View)
block, err := wstore.DBGet[*wstore.Block](ctx, blockId)
if err != nil {
@ -308,7 +308,7 @@ func ProcessStaticCommand(blockId string, cmdGen BlockCommand) error {
return fmt.Errorf("error updating block: %w", err)
}
return nil
case *SetMetaCommand:
case *BlockSetMetaCommand:
log.Printf("SETMETA: %s | %v\n", blockId, cmd.Meta)
block, err := wstore.DBGet[*wstore.Block](ctx, blockId)
if err != nil {

View File

@ -9,25 +9,21 @@ import (
"time"
"github.com/wavetermdev/thenextwave/pkg/blockcontroller"
"github.com/wavetermdev/thenextwave/pkg/service/servicemeta"
"github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
)
type BlockService struct{}
const DefaultTimeout = 2 * time.Second
func (bs *BlockService) SendCommand_Meta() servicemeta.MethodMeta {
return servicemeta.MethodMeta{
func (bs *BlockService) SendCommand_Meta() tsgenmeta.MethodMeta {
return tsgenmeta.MethodMeta{
Desc: "send command to block",
ArgNames: []string{"blockid", "command"},
ArgNames: []string{"blockid", "cmd"},
}
}
func (bs *BlockService) SendCommand(blockId string, cmdMap map[string]any) error {
cmd, err := blockcontroller.ParseCmdMap(cmdMap)
if err != nil {
return fmt.Errorf("error parsing command map: %w", err)
}
func (bs *BlockService) SendCommand(blockId string, cmd blockcontroller.BlockCommand) error {
if strings.HasPrefix(cmd.GetCommand(), "controller:") {
bc := blockcontroller.GetBlockController(blockId)
if bc == nil {

View File

@ -12,7 +12,7 @@ import (
"time"
"github.com/wavetermdev/thenextwave/pkg/blockcontroller"
"github.com/wavetermdev/thenextwave/pkg/service/servicemeta"
"github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
"github.com/wavetermdev/thenextwave/pkg/waveobj"
"github.com/wavetermdev/thenextwave/pkg/wstore"
)
@ -29,8 +29,8 @@ func parseORef(oref string) (*waveobj.ORef, error) {
return &waveobj.ORef{OType: fields[0], OID: fields[1]}, nil
}
func (svc *ObjectService) GetObject_Meta() servicemeta.MethodMeta {
return servicemeta.MethodMeta{
func (svc *ObjectService) GetObject_Meta() tsgenmeta.MethodMeta {
return tsgenmeta.MethodMeta{
Desc: "get wave object by oref",
ArgNames: []string{"oref"},
}
@ -50,8 +50,8 @@ func (svc *ObjectService) GetObject(orefStr string) (waveobj.WaveObj, error) {
return obj, nil
}
func (svc *ObjectService) GetObjects_Meta() servicemeta.MethodMeta {
return servicemeta.MethodMeta{
func (svc *ObjectService) GetObjects_Meta() tsgenmeta.MethodMeta {
return tsgenmeta.MethodMeta{
ArgNames: []string{"orefs"},
ReturnDesc: "objects",
}
@ -92,8 +92,8 @@ func updatesRtn(ctx context.Context, rtnVal map[string]any) (any, error) {
return rtnVal, nil
}
func (svc *ObjectService) AddTabToWorkspace_Meta() servicemeta.MethodMeta {
return servicemeta.MethodMeta{
func (svc *ObjectService) AddTabToWorkspace_Meta() tsgenmeta.MethodMeta {
return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "tabName", "activateTab"},
ReturnDesc: "tabId",
}
@ -120,8 +120,8 @@ func (svc *ObjectService) AddTabToWorkspace(uiContext wstore.UIContext, tabName
return tab.OID, wstore.ContextGetUpdatesRtn(ctx), nil
}
func (svc *ObjectService) SetActiveTab_Meta() servicemeta.MethodMeta {
return servicemeta.MethodMeta{
func (svc *ObjectService) SetActiveTab_Meta() tsgenmeta.MethodMeta {
return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "tabId"},
}
}
@ -158,8 +158,8 @@ func (svc *ObjectService) SetActiveTab(uiContext wstore.UIContext, tabId string)
return updates, nil
}
func (svc *ObjectService) CreateBlock_Meta() servicemeta.MethodMeta {
return servicemeta.MethodMeta{
func (svc *ObjectService) CreateBlock_Meta() tsgenmeta.MethodMeta {
return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "blockDef", "rtOpts"},
ReturnDesc: "blockId",
}
@ -185,8 +185,8 @@ func (svc *ObjectService) CreateBlock(uiContext wstore.UIContext, blockDef *wsto
return blockData.OID, wstore.ContextGetUpdatesRtn(ctx), nil
}
func (svc *ObjectService) DeleteBlock_Meta() servicemeta.MethodMeta {
return servicemeta.MethodMeta{
func (svc *ObjectService) DeleteBlock_Meta() tsgenmeta.MethodMeta {
return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "blockId"},
}
}
@ -203,8 +203,8 @@ func (svc *ObjectService) DeleteBlock(uiContext wstore.UIContext, blockId string
return wstore.ContextGetUpdatesRtn(ctx), nil
}
func (svc *ObjectService) CloseTab_Meta() servicemeta.MethodMeta {
return servicemeta.MethodMeta{
func (svc *ObjectService) CloseTab_Meta() tsgenmeta.MethodMeta {
return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "tabId"},
}
}
@ -244,8 +244,8 @@ func (svc *ObjectService) CloseTab(uiContext wstore.UIContext, tabId string) (ws
return wstore.ContextGetUpdatesRtn(ctx), nil
}
func (svc *ObjectService) UpdateObjectMeta_Meta() servicemeta.MethodMeta {
return servicemeta.MethodMeta{
func (svc *ObjectService) UpdateObjectMeta_Meta() tsgenmeta.MethodMeta {
return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "oref", "meta"},
}
}
@ -265,8 +265,8 @@ func (svc *ObjectService) UpdateObjectMeta(uiContext wstore.UIContext, orefStr s
return wstore.ContextGetUpdatesRtn(ctx), nil
}
func (svc *ObjectService) UpdateObject_Meta() servicemeta.MethodMeta {
return servicemeta.MethodMeta{
func (svc *ObjectService) UpdateObject_Meta() tsgenmeta.MethodMeta {
return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "waveObj", "returnUpdates"},
}
}

View File

@ -9,11 +9,12 @@ import (
"reflect"
"strings"
"github.com/wavetermdev/thenextwave/pkg/blockcontroller"
"github.com/wavetermdev/thenextwave/pkg/service/blockservice"
"github.com/wavetermdev/thenextwave/pkg/service/clientservice"
"github.com/wavetermdev/thenextwave/pkg/service/fileservice"
"github.com/wavetermdev/thenextwave/pkg/service/objectservice"
"github.com/wavetermdev/thenextwave/pkg/service/servicemeta"
"github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
"github.com/wavetermdev/thenextwave/pkg/waveobj"
"github.com/wavetermdev/thenextwave/pkg/wstore"
)
@ -31,9 +32,10 @@ var updatesRType = reflect.TypeOf(([]wstore.WaveObjUpdate{}))
var waveObjRType = reflect.TypeOf((*waveobj.WaveObj)(nil)).Elem()
var waveObjSliceRType = reflect.TypeOf([]waveobj.WaveObj{})
var waveObjMapRType = reflect.TypeOf(map[string]waveobj.WaveObj{})
var methodMetaRType = reflect.TypeOf(servicemeta.MethodMeta{})
var methodMetaRType = reflect.TypeOf(tsgenmeta.MethodMeta{})
var waveObjUpdateRType = reflect.TypeOf(wstore.WaveObjUpdate{})
var uiContextRType = reflect.TypeOf((*wstore.UIContext)(nil)).Elem()
var blockCommandRType = reflect.TypeOf((*blockcontroller.BlockCommand)(nil)).Elem()
type WebCallType struct {
Service string `json:"service"`
@ -89,12 +91,25 @@ func convertComplex(argType reflect.Type, jsonArg any) (any, error) {
}
func isSpecialWaveArgType(argType reflect.Type) bool {
return argType == waveObjRType || argType == waveObjSliceRType || argType == waveObjMapRType
return argType == waveObjRType || argType == waveObjSliceRType || argType == waveObjMapRType || argType == blockCommandRType
}
func convertBlockCommand(argType reflect.Type, jsonArg any) (any, error) {
if _, ok := jsonArg.(map[string]any); !ok {
return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
}
cmd, err := blockcontroller.ParseCmdMap(jsonArg.(map[string]any))
if err != nil {
return nil, fmt.Errorf("error parsing command map: %w", err)
}
return cmd, nil
}
func convertSpecial(argType reflect.Type, jsonArg any) (any, error) {
jsonType := reflect.TypeOf(jsonArg)
if argType == waveObjRType {
if argType == blockCommandRType {
return convertBlockCommand(argType, jsonArg)
} else if argType == waveObjRType {
if jsonType.Kind() != reflect.Map {
return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
}

View File

@ -1,10 +0,0 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package servicemeta
type MethodMeta struct {
Desc string
ArgNames []string
ReturnDesc string
}

View File

@ -10,9 +10,10 @@ import (
"reflect"
"strings"
"github.com/wavetermdev/thenextwave/pkg/blockcontroller"
"github.com/wavetermdev/thenextwave/pkg/eventbus"
"github.com/wavetermdev/thenextwave/pkg/service"
"github.com/wavetermdev/thenextwave/pkg/service/servicemeta"
"github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
"github.com/wavetermdev/thenextwave/pkg/waveobj"
"github.com/wavetermdev/thenextwave/pkg/wstore"
)
@ -69,7 +70,7 @@ func isFieldOmitEmpty(field reflect.StructField) bool {
return false
}
func TypeToTSType(t reflect.Type) (string, []reflect.Type) {
func TypeToTSType(t reflect.Type, tsTypesMap map[reflect.Type]string) (string, []reflect.Type) {
switch t.Kind() {
case reflect.String:
return "string", nil
@ -80,7 +81,7 @@ func TypeToTSType(t reflect.Type) (string, []reflect.Type) {
case reflect.Bool:
return "boolean", nil
case reflect.Slice, reflect.Array:
elemType, subTypes := TypeToTSType(t.Elem())
elemType, subTypes := TypeToTSType(t.Elem(), tsTypesMap)
if elemType == "" {
return "", nil
}
@ -92,7 +93,7 @@ func TypeToTSType(t reflect.Type) (string, []reflect.Type) {
if t == metaRType {
return "MetaType", nil
}
elemType, subTypes := TypeToTSType(t.Elem())
elemType, subTypes := TypeToTSType(t.Elem(), tsTypesMap)
if elemType == "" {
return "", nil
}
@ -100,10 +101,10 @@ func TypeToTSType(t reflect.Type) (string, []reflect.Type) {
case reflect.Struct:
return t.Name(), []reflect.Type{t}
case reflect.Ptr:
return TypeToTSType(t.Elem())
return TypeToTSType(t.Elem(), tsTypesMap)
case reflect.Interface:
if t == waveObjRType {
return "WaveObj", nil
if _, ok := tsTypesMap[t]; ok {
return t.Name(), nil
}
return "any", nil
default:
@ -115,7 +116,7 @@ var tsRenameMap = map[string]string{
"Window": "WaveWindow",
}
func generateTSTypeInternal(rtype reflect.Type) (string, []reflect.Type) {
func generateTSTypeInternal(rtype reflect.Type, tsTypesMap map[reflect.Type]string) (string, []reflect.Type) {
var buf bytes.Buffer
waveObjType := reflect.TypeOf((*waveobj.WaveObj)(nil)).Elem()
tsTypeName := rtype.Name()
@ -149,10 +150,10 @@ func generateTSTypeInternal(rtype reflect.Type) (string, []reflect.Type) {
}
tsTypeTag := field.Tag.Get("tstype")
if tsTypeTag != "" {
buf.WriteString(fmt.Sprintf(" %s%s: %s;\n", fieldName, optMarker, tsTypeTag))
buf.WriteString(fmt.Sprintf(" %s%s: %s;\n", fieldName, optMarker, tsTypeTag))
continue
}
tsType, fieldSubTypes := TypeToTSType(field.Type)
tsType, fieldSubTypes := TypeToTSType(field.Type, tsTypesMap)
if tsType == "" {
continue
}
@ -179,7 +180,32 @@ func GenerateWaveObjTSType() string {
func GenerateMetaType() string {
return "type MetaType = {[key: string]: any}\n"
}
func GenerateTSTypeUnion(unionMeta tsgenmeta.TypeUnionMeta, tsTypeMap map[reflect.Type]string) {
rtn := generateTSTypeUnionInternal(unionMeta)
tsTypeMap[unionMeta.BaseType] = rtn
for _, rtype := range unionMeta.Types {
GenerateTSType(rtype, tsTypeMap)
}
}
func generateTSTypeUnionInternal(unionMeta tsgenmeta.TypeUnionMeta) string {
var buf bytes.Buffer
if unionMeta.Desc != "" {
buf.WriteString(fmt.Sprintf("// %s\n", unionMeta.Desc))
}
buf.WriteString(fmt.Sprintf("type %s = {\n", unionMeta.BaseType.Name()))
buf.WriteString(fmt.Sprintf(" %s: string;\n", unionMeta.TypeFieldName))
buf.WriteString("} & ( ")
for idx, rtype := range unionMeta.Types {
if idx > 0 {
buf.WriteString(" | ")
}
buf.WriteString(rtype.Name())
}
buf.WriteString(" );\n")
return buf.String()
}
func GenerateTSType(rtype reflect.Type, tsTypesMap map[reflect.Type]string) {
@ -212,7 +238,7 @@ func GenerateTSType(rtype reflect.Type, tsTypesMap map[reflect.Type]string) {
if rtype.Kind() != reflect.Struct {
return
}
tsType, subTypes := generateTSTypeInternal(rtype)
tsType, subTypes := generateTSTypeInternal(rtype, tsTypesMap)
tsTypesMap[rtype] = tsType
for _, subType := range subTypes {
GenerateTSType(subType, tsTypesMap)
@ -229,7 +255,7 @@ func hasUpdatesReturn(method reflect.Method) bool {
return false
}
func GenerateMethodSignature(serviceName string, method reflect.Method, meta servicemeta.MethodMeta, isFirst bool) string {
func GenerateMethodSignature(serviceName string, method reflect.Method, meta tsgenmeta.MethodMeta, isFirst bool, tsTypesMap map[reflect.Type]string) string {
var sb strings.Builder
mayReturnUpdates := hasUpdatesReturn(method)
if (meta.Desc != "" || meta.ReturnDesc != "" || mayReturnUpdates) && !isFirst {
@ -260,7 +286,7 @@ func GenerateMethodSignature(serviceName string, method reflect.Method, meta ser
if inType == contextRType || inType == uiContextRType {
continue
}
tsTypeName, _ := TypeToTSType(inType)
tsTypeName, _ := TypeToTSType(inType, tsTypesMap)
var argName string
if idx-1 < len(meta.ArgNames) {
argName = meta.ArgNames[idx-1] // subtract 1 for receiver
@ -280,7 +306,7 @@ func GenerateMethodSignature(serviceName string, method reflect.Method, meta ser
if outType == updatesRtnRType {
continue
}
tsTypeName, _ := TypeToTSType(outType)
tsTypeName, _ := TypeToTSType(outType, tsTypesMap)
sb.WriteString(fmt.Sprintf("Promise<%s>", tsTypeName))
wroteRtn = true
}
@ -291,11 +317,11 @@ func GenerateMethodSignature(serviceName string, method reflect.Method, meta ser
return sb.String()
}
func GenerateMethodBody(serviceName string, method reflect.Method, meta servicemeta.MethodMeta) string {
func GenerateMethodBody(serviceName string, method reflect.Method, meta tsgenmeta.MethodMeta) string {
return fmt.Sprintf(" return WOS.callBackendService(%q, %q, Array.from(arguments))\n", serviceName, method.Name)
}
func GenerateServiceClass(serviceName string, serviceObj any) string {
func GenerateServiceClass(serviceName string, serviceObj any, tsTypesMap map[reflect.Type]string) string {
serviceType := reflect.TypeOf(serviceObj)
var sb strings.Builder
tsServiceName := serviceType.Elem().Name()
@ -309,14 +335,14 @@ func GenerateServiceClass(serviceName string, serviceObj any) string {
if strings.HasSuffix(method.Name, "_Meta") {
continue
}
var meta servicemeta.MethodMeta
var meta tsgenmeta.MethodMeta
metaMethod, found := serviceType.MethodByName(method.Name + "_Meta")
if found {
serviceObjVal := reflect.ValueOf(serviceObj)
metaVal := metaMethod.Func.Call([]reflect.Value{serviceObjVal})
meta = metaVal[0].Interface().(servicemeta.MethodMeta)
meta = metaVal[0].Interface().(tsgenmeta.MethodMeta)
}
sb.WriteString(GenerateMethodSignature(serviceName, method, meta, isFirst))
sb.WriteString(GenerateMethodSignature(serviceName, method, meta, isFirst, tsTypesMap))
sb.WriteString(GenerateMethodBody(serviceName, method, meta))
sb.WriteString(" }\n")
isFirst = false
@ -327,6 +353,7 @@ func GenerateServiceClass(serviceName string, serviceObj any) string {
}
func GenerateWaveObjTypes(tsTypesMap map[reflect.Type]string) {
GenerateTSTypeUnion(blockcontroller.CommandTypeUnionMeta(), tsTypesMap)
GenerateTSType(reflect.TypeOf(waveobj.ORef{}), tsTypesMap)
GenerateTSType(reflect.TypeOf((*waveobj.WaveObj)(nil)).Elem(), tsTypesMap)
GenerateTSType(reflect.TypeOf(map[string]any{}), tsTypesMap)

View File

@ -0,0 +1,19 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package tsgenmeta
import "reflect"
type MethodMeta struct {
Desc string
ArgNames []string
ReturnDesc string
}
type TypeUnionMeta struct {
BaseType reflect.Type
Desc string
TypeFieldName string
Types []reflect.Type
}