// Copyright 2024, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 package wshutil import ( "encoding/json" "fmt" "reflect" "github.com/wavetermdev/thenextwave/pkg/ijson" "github.com/wavetermdev/thenextwave/pkg/shellexec" "github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta" "github.com/wavetermdev/thenextwave/pkg/wstore" ) const CommandKey = "command" const ( BlockCommand_Message = "message" BlockCommand_SetView = "setview" BlockCommand_SetMeta = "setmeta" BlockCommand_GetMeta = "getmeta" BlockCommand_Input = "controller:input" BlockCommand_AppendBlockFile = "blockfile:append" BlockCommand_AppendIJson = "blockfile:appendijson" Command_ResolveIds = "resolveids" Command_CreateBlock = "createblock" ) var CommandToTypeMap = map[string]reflect.Type{ BlockCommand_Input: reflect.TypeOf(BlockInputCommand{}), BlockCommand_SetView: reflect.TypeOf(BlockSetViewCommand{}), BlockCommand_SetMeta: reflect.TypeOf(BlockSetMetaCommand{}), BlockCommand_GetMeta: reflect.TypeOf(BlockGetMetaCommand{}), BlockCommand_Message: reflect.TypeOf(BlockMessageCommand{}), BlockCommand_AppendBlockFile: reflect.TypeOf(BlockAppendFileCommand{}), BlockCommand_AppendIJson: reflect.TypeOf(BlockAppendIJsonCommand{}), Command_ResolveIds: reflect.TypeOf(ResolveIdsCommand{}), Command_CreateBlock: reflect.TypeOf(CreateBlockCommand{}), } func CommandTypeUnionMeta() tsgenmeta.TypeUnionMeta { var rtypes []reflect.Type for _, rtype := range CommandToTypeMap { rtypes = append(rtypes, rtype) } return tsgenmeta.TypeUnionMeta{ BaseType: reflect.TypeOf((*BlockCommand)(nil)).Elem(), TypeFieldName: "command", Types: rtypes, } } type baseCommand struct { Command string `json:"command"` } type BlockCommand interface { GetCommand() string } type BlockCommandWrapper struct { BlockCommand } func ParseCmdMap(cmdMap map[string]any) (BlockCommand, error) { cmdType, ok := cmdMap[CommandKey].(string) if !ok { return nil, fmt.Errorf("no %s field in command map", CommandKey) } mapJson, err := json.Marshal(cmdMap) if err != nil { return nil, fmt.Errorf("error marshalling command map: %w", err) } rtype := CommandToTypeMap[cmdType] if rtype == nil { return nil, fmt.Errorf("unknown command type %q", cmdType) } cmd := reflect.New(rtype).Interface() err = json.Unmarshal(mapJson, cmd) if err != nil { return nil, fmt.Errorf("error unmarshalling command: %w", err) } return cmd.(BlockCommand), nil } 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 *BlockInputCommand) GetCommand() string { return BlockCommand_Input } type ResolveIdsCommand struct { Command string `json:"command" tstype:"\"resolveids\""` Ids []string `json:"ids"` } func (ric *ResolveIdsCommand) GetCommand() string { return Command_ResolveIds } type BlockSetViewCommand struct { Command string `json:"command" tstype:"\"setview\""` View string `json:"view"` } func (svc *BlockSetViewCommand) GetCommand() string { return BlockCommand_SetView } type BlockGetMetaCommand struct { Command string `json:"command" tstype:"\"getmeta\""` ORef string `json:"oref"` // oref string } func (gmc *BlockGetMetaCommand) GetCommand() string { return BlockCommand_GetMeta } type BlockSetMetaCommand struct { Command string `json:"command" tstype:"\"setmeta\""` ORef string `json:"oref,omitempty"` // allows oref, 8-char oid, or full uuid (empty is current block) Meta map[string]any `json:"meta"` } func (smc *BlockSetMetaCommand) GetCommand() string { return BlockCommand_SetMeta } type BlockMessageCommand struct { Command string `json:"command" tstype:"\"message\""` Message string `json:"message"` } func (bmc *BlockMessageCommand) GetCommand() string { return BlockCommand_Message } type BlockAppendFileCommand struct { Command string `json:"command" tstype:"\"blockfile:append\""` FileName string `json:"filename"` Data []byte `json:"data"` } func (bwc *BlockAppendFileCommand) GetCommand() string { return BlockCommand_AppendBlockFile } type BlockAppendIJsonCommand struct { Command string `json:"command" tstype:"\"blockfile:appendijson\""` FileName string `json:"filename"` Data ijson.Command `json:"data"` } func (bwc *BlockAppendIJsonCommand) GetCommand() string { return BlockCommand_AppendIJson } type CreateBlockCommand struct { Command string `json:"command" tstype:"\"createblock\""` BlockDef *wstore.BlockDef `json:"blockdef"` RtOpts *wstore.RuntimeOpts `json:"rtopts,omitempty"` } func (cbc *CreateBlockCommand) GetCommand() string { return Command_CreateBlock }