mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
new resolver formats (tab:N), and also make the structure of the resolvers much more robust (#1254)
This commit is contained in:
parent
58f2f4ae8e
commit
2c055b56d0
@ -21,18 +21,13 @@ func init() {
|
||||
|
||||
func deleteBlockRun(cmd *cobra.Command, args []string) {
|
||||
oref := blockArg
|
||||
err := validateEasyORef(oref)
|
||||
fullORef, err := resolveSimpleId(oref)
|
||||
if err != nil {
|
||||
WriteStderr("[error] %v\n", err)
|
||||
return
|
||||
}
|
||||
fullORef, err := resolveSimpleId(oref)
|
||||
if err != nil {
|
||||
WriteStderr("[error] resolving oref: %v\n", err)
|
||||
return
|
||||
}
|
||||
if fullORef.OType != "block" {
|
||||
WriteStderr("[error] oref is not a block\n")
|
||||
WriteStderr("[error] object reference is not a block\n")
|
||||
return
|
||||
}
|
||||
deleteBlockData := &wshrpc.CommandDeleteBlockData{
|
||||
|
@ -74,14 +74,9 @@ func getMetaRun(cmd *cobra.Command, args []string) {
|
||||
WriteStderr("[error] oref is required")
|
||||
return
|
||||
}
|
||||
err := validateEasyORef(oref)
|
||||
if err != nil {
|
||||
WriteStderr("[error] %v\n", err)
|
||||
return
|
||||
}
|
||||
fullORef, err := resolveSimpleId(oref)
|
||||
if err != nil {
|
||||
WriteStderr("[error] resolving oref: %v\n", err)
|
||||
WriteStderr("[error] %v\n", err)
|
||||
return
|
||||
}
|
||||
resp, err := wshclient.GetMetaCommand(RpcClient, wshrpc.CommandGetMetaData{ORef: *fullORef}, &wshrpc.RpcOpts{Timeout: 2000})
|
||||
|
@ -29,14 +29,9 @@ func runReadFile(cmd *cobra.Command, args []string) {
|
||||
WriteStderr("[error] oref is required\n")
|
||||
return
|
||||
}
|
||||
err := validateEasyORef(oref)
|
||||
if err != nil {
|
||||
WriteStderr("[error] %v\n", err)
|
||||
return
|
||||
}
|
||||
fullORef, err := resolveSimpleId(oref)
|
||||
if err != nil {
|
||||
WriteStderr("error resolving oref: %v\n", err)
|
||||
WriteStderr("[error] %v\n", err)
|
||||
return
|
||||
}
|
||||
resp64, err := wshclient.FileReadCommand(RpcClient, wshrpc.CommandFileData{ZoneId: fullORef.OID, FileName: args[1]}, &wshrpc.RpcOpts{Timeout: 5000})
|
||||
|
@ -9,11 +9,9 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
@ -76,10 +74,6 @@ func resolveBlockArg() (*waveobj.ORef, error) {
|
||||
if oref == "" {
|
||||
return nil, fmt.Errorf("blockid is required")
|
||||
}
|
||||
err := validateEasyORef(oref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fullORef, err := resolveSimpleId(oref)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolving blockid: %w", err)
|
||||
@ -128,33 +122,6 @@ func setTermHtmlMode() {
|
||||
|
||||
var oidRe = regexp.MustCompile(`^[0-9a-f]{8}$`)
|
||||
|
||||
func validateEasyORef(oref string) error {
|
||||
if oref == "this" || oref == "tab" {
|
||||
return nil
|
||||
}
|
||||
if num, err := strconv.Atoi(oref); err == nil && num >= 1 {
|
||||
return nil
|
||||
}
|
||||
if strings.Contains(oref, ":") {
|
||||
_, err := waveobj.ParseORef(oref)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid ORef: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if len(oref) == 8 {
|
||||
if !oidRe.MatchString(oref) {
|
||||
return fmt.Errorf("invalid short OID format, must only use 0-9a-f: %q", oref)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
_, err := uuid.Parse(oref)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid object reference (must be UUID, or a positive integer): %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isFullORef(orefStr string) bool {
|
||||
_, err := waveobj.ParseORef(orefStr)
|
||||
return err == nil
|
||||
|
@ -112,14 +112,9 @@ func setMetaRun(cmd *cobra.Command, args []string) {
|
||||
WriteStderr("[error] block (oref) is required\n")
|
||||
return
|
||||
}
|
||||
err := validateEasyORef(blockArg)
|
||||
if err != nil {
|
||||
WriteStderr("[error] %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
var jsonMeta map[string]interface{}
|
||||
if setMetaJsonFilePath != "" {
|
||||
var err error
|
||||
jsonMeta, err = loadJSONFile(setMetaJsonFilePath)
|
||||
if err != nil {
|
||||
WriteStderr("[error] %v\n", err)
|
||||
@ -146,7 +141,7 @@ func setMetaRun(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
fullORef, err := resolveSimpleId(blockArg)
|
||||
if err != nil {
|
||||
WriteStderr("[error] resolving oref: %v\n", err)
|
||||
WriteStderr("[error] %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -55,10 +55,6 @@ func webGetRun(cmd *cobra.Command, args []string) error {
|
||||
if oref == "" {
|
||||
return fmt.Errorf("blockid not specified")
|
||||
}
|
||||
err := validateEasyORef(oref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fullORef, err := resolveSimpleId(oref)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resolving blockid: %w", err)
|
||||
|
@ -86,6 +86,9 @@ func ParseORef(orefStr string) (ORef, error) {
|
||||
if !otypeRe.MatchString(otype) {
|
||||
return ORef{}, fmt.Errorf("invalid object type: %q", otype)
|
||||
}
|
||||
if !ValidOTypes[otype] {
|
||||
return ORef{}, fmt.Errorf("unknown object type: %q", otype)
|
||||
}
|
||||
oid := fields[1]
|
||||
_, err := uuid.Parse(oid)
|
||||
if err != nil {
|
||||
|
@ -30,6 +30,15 @@ const (
|
||||
OType_Block = "block"
|
||||
)
|
||||
|
||||
var ValidOTypes = map[string]bool{
|
||||
OType_Client: true,
|
||||
OType_Window: true,
|
||||
OType_Workspace: true,
|
||||
OType_Tab: true,
|
||||
OType_LayoutState: true,
|
||||
OType_Block: true,
|
||||
}
|
||||
|
||||
type WaveObjUpdate struct {
|
||||
UpdateType string `json:"updatetype"`
|
||||
OType string `json:"otype"`
|
||||
|
198
pkg/wshrpc/wshserver/resolvers.go
Normal file
198
pkg/wshrpc/wshserver/resolvers.go
Normal file
@ -0,0 +1,198 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package wshserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
"github.com/wavetermdev/waveterm/pkg/wstore"
|
||||
)
|
||||
|
||||
const SimpleId_This = "this"
|
||||
const SimpleId_Tab = "tab"
|
||||
|
||||
var (
|
||||
simpleTabNumRe = regexp.MustCompile(`^tab:(\d{1,3})$`)
|
||||
shortUUIDRe = regexp.MustCompile(`^[0-9a-f]{8}$`)
|
||||
SimpleId_BlockNum_Regex = regexp.MustCompile(`^\d+$`)
|
||||
)
|
||||
|
||||
// Helper function to validate UUIDs or 8-char UUIDs format
|
||||
func isValidSimpleUUID(s string) bool {
|
||||
// Try parsing as full UUID
|
||||
_, err := uuid.Parse(s)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if it's an 8-char hex prefix
|
||||
shortUUIDPattern := regexp.MustCompile(`^[0-9a-f]{8}$`)
|
||||
return shortUUIDPattern.MatchString(strings.ToLower(s))
|
||||
}
|
||||
|
||||
// First function: detect/choose discriminator
|
||||
func parseSimpleId(simpleId string) (discriminator string, value string, err error) {
|
||||
// Check for explicit discriminator with @
|
||||
if parts := strings.SplitN(simpleId, "@", 2); len(parts) == 2 {
|
||||
return parts[0], parts[1], nil
|
||||
}
|
||||
|
||||
// Handle special keywords
|
||||
if simpleId == SimpleId_This || simpleId == SimpleId_Tab {
|
||||
return "this", simpleId, nil
|
||||
}
|
||||
|
||||
// Check if it's a simple ORef (type:uuid)
|
||||
if _, err := waveobj.ParseORef(simpleId); err == nil {
|
||||
return "oref", simpleId, nil
|
||||
}
|
||||
|
||||
// Check for tab:N format
|
||||
if simpleTabNumRe.MatchString(simpleId) {
|
||||
return "tabnum", simpleId, nil
|
||||
}
|
||||
|
||||
// Check for plain number (block reference)
|
||||
if _, err := strconv.Atoi(simpleId); err == nil {
|
||||
return "blocknum", simpleId, nil
|
||||
}
|
||||
|
||||
// Check for UUIDs
|
||||
if _, err := uuid.Parse(simpleId); err == nil {
|
||||
return "uuid", simpleId, nil
|
||||
}
|
||||
if shortUUIDRe.MatchString(strings.ToLower(simpleId)) {
|
||||
return "uuid8", simpleId, nil
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("invalid simple id format: %s", simpleId)
|
||||
}
|
||||
|
||||
// Individual resolvers
|
||||
func resolveThis(ctx context.Context, data wshrpc.CommandResolveIdsData, value string) (*waveobj.ORef, error) {
|
||||
if data.BlockId == "" {
|
||||
return nil, fmt.Errorf("no blockid in request")
|
||||
}
|
||||
|
||||
if value == SimpleId_This {
|
||||
return &waveobj.ORef{OType: waveobj.OType_Block, OID: data.BlockId}, nil
|
||||
}
|
||||
if value == SimpleId_Tab {
|
||||
tabId, err := wstore.DBFindTabForBlockId(ctx, data.BlockId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error finding tab: %v", err)
|
||||
}
|
||||
return &waveobj.ORef{OType: waveobj.OType_Tab, OID: tabId}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid value for 'this' resolver: %s", value)
|
||||
}
|
||||
|
||||
func resolveORef(ctx context.Context, value string) (*waveobj.ORef, error) {
|
||||
parsedORef, err := waveobj.ParseORef(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing oref: %v", err)
|
||||
}
|
||||
return &parsedORef, nil
|
||||
}
|
||||
|
||||
func resolveTabNum(ctx context.Context, data wshrpc.CommandResolveIdsData, value string) (*waveobj.ORef, error) {
|
||||
m := simpleTabNumRe.FindStringSubmatch(value)
|
||||
if m == nil {
|
||||
return nil, fmt.Errorf("error parsing simple tab id: %s", value)
|
||||
}
|
||||
|
||||
tabNum, err := strconv.Atoi(m[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing simple tab num: %v", err)
|
||||
}
|
||||
|
||||
curTabId, err := wstore.DBFindTabForBlockId(ctx, data.BlockId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error finding tab for block: %v", err)
|
||||
}
|
||||
|
||||
wsId, err := wstore.DBFindWorkspaceForTabId(ctx, curTabId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error finding current workspace: %v", err)
|
||||
}
|
||||
|
||||
ws, err := wstore.DBMustGet[*waveobj.Workspace](ctx, wsId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting workspace: %v", err)
|
||||
}
|
||||
|
||||
if tabNum < 1 || tabNum > len(ws.TabIds) {
|
||||
return nil, fmt.Errorf("tab num out of range, workspace has %d tabs", len(ws.TabIds))
|
||||
}
|
||||
|
||||
resolvedTabId := ws.TabIds[tabNum-1]
|
||||
return &waveobj.ORef{OType: waveobj.OType_Tab, OID: resolvedTabId}, nil
|
||||
}
|
||||
|
||||
func resolveBlock(ctx context.Context, data wshrpc.CommandResolveIdsData, value string) (*waveobj.ORef, error) {
|
||||
blockNum, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing block number: %v", err)
|
||||
}
|
||||
|
||||
tabId, err := wstore.DBFindTabForBlockId(ctx, data.BlockId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error finding tab for blockid %s: %w", data.BlockId, err)
|
||||
}
|
||||
|
||||
tab, err := wstore.DBGet[*waveobj.Tab](ctx, tabId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error retrieving tab %s: %w", tabId, err)
|
||||
}
|
||||
|
||||
layout, err := wstore.DBGet[*waveobj.LayoutState](ctx, tab.LayoutState)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error retrieving layout state %s: %w", tab.LayoutState, err)
|
||||
}
|
||||
|
||||
if layout.LeafOrder == nil {
|
||||
return nil, fmt.Errorf("could not resolve block num %v, leaf order is empty", blockNum)
|
||||
}
|
||||
|
||||
leafIndex := blockNum - 1 // block nums are 1-indexed
|
||||
if len(*layout.LeafOrder) <= leafIndex {
|
||||
return nil, fmt.Errorf("could not find a node in the layout matching blockNum %v", blockNum)
|
||||
}
|
||||
|
||||
leafEntry := (*layout.LeafOrder)[leafIndex]
|
||||
return &waveobj.ORef{OType: waveobj.OType_Block, OID: leafEntry.BlockId}, nil
|
||||
}
|
||||
|
||||
func resolveUUID(ctx context.Context, value string) (*waveobj.ORef, error) {
|
||||
return wstore.DBResolveEasyOID(ctx, value)
|
||||
}
|
||||
|
||||
// Main resolver function
|
||||
func resolveSimpleId(ctx context.Context, data wshrpc.CommandResolveIdsData, simpleId string) (*waveobj.ORef, error) {
|
||||
discriminator, value, err := parseSimpleId(simpleId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch discriminator {
|
||||
case "this":
|
||||
return resolveThis(ctx, data, value)
|
||||
case "oref":
|
||||
return resolveORef(ctx, value)
|
||||
case "tabnum":
|
||||
return resolveTabNum(ctx, data, value)
|
||||
case "blocknum":
|
||||
return resolveBlock(ctx, data, value)
|
||||
case "uuid", "uuid8":
|
||||
return resolveUUID(ctx, value)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown discriminator: %s", discriminator)
|
||||
}
|
||||
}
|
@ -12,8 +12,6 @@ import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -35,10 +33,6 @@ import (
|
||||
"github.com/wavetermdev/waveterm/pkg/wstore"
|
||||
)
|
||||
|
||||
const SimpleId_This = "this"
|
||||
const SimpleId_Tab = "tab"
|
||||
|
||||
var SimpleId_BlockNum_Regex = regexp.MustCompile(`^\d+$`)
|
||||
var InvalidWslDistroNames = []string{"docker-desktop", "docker-desktop-data"}
|
||||
|
||||
type WshServer struct{}
|
||||
@ -155,70 +149,26 @@ func sendWaveObjUpdate(oref waveobj.ORef) {
|
||||
})
|
||||
}
|
||||
|
||||
func resolveSimpleId(ctx context.Context, data wshrpc.CommandResolveIdsData, simpleId string) (*waveobj.ORef, error) {
|
||||
if simpleId == SimpleId_This {
|
||||
if data.BlockId == "" {
|
||||
return nil, fmt.Errorf("no blockid in request")
|
||||
}
|
||||
return &waveobj.ORef{OType: waveobj.OType_Block, OID: data.BlockId}, nil
|
||||
}
|
||||
if simpleId == SimpleId_Tab {
|
||||
if data.BlockId == "" {
|
||||
return nil, fmt.Errorf("no blockid in request")
|
||||
}
|
||||
tabId, err := wstore.DBFindTabForBlockId(ctx, data.BlockId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error finding tab: %v", err)
|
||||
}
|
||||
return &waveobj.ORef{OType: waveobj.OType_Tab, OID: tabId}, nil
|
||||
}
|
||||
blockNum, err := strconv.Atoi(simpleId)
|
||||
if err == nil {
|
||||
tabId, err := wstore.DBFindTabForBlockId(ctx, data.BlockId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error finding tab for blockid %s: %w", data.BlockId, err)
|
||||
}
|
||||
|
||||
tab, err := wstore.DBGet[*waveobj.Tab](ctx, tabId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error retrieving tab %s: %w", tabId, err)
|
||||
}
|
||||
|
||||
layout, err := wstore.DBGet[*waveobj.LayoutState](ctx, tab.LayoutState)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error retrieving layout state %s: %w", tab.LayoutState, err)
|
||||
}
|
||||
|
||||
if layout.LeafOrder == nil {
|
||||
return nil, fmt.Errorf("could not resolve block num %v, leaf order is empty", blockNum)
|
||||
}
|
||||
|
||||
leafIndex := blockNum - 1 // block nums are 1-indexed, we need the 0-indexed version
|
||||
if len(*layout.LeafOrder) <= leafIndex {
|
||||
return nil, fmt.Errorf("could not find a node in the layout matching blockNum %v", blockNum)
|
||||
}
|
||||
leafEntry := (*layout.LeafOrder)[leafIndex]
|
||||
return &waveobj.ORef{OType: waveobj.OType_Block, OID: leafEntry.BlockId}, nil
|
||||
} else if strings.Contains(simpleId, ":") {
|
||||
rtn, err := waveobj.ParseORef(simpleId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing simple id: %w", err)
|
||||
}
|
||||
return &rtn, nil
|
||||
}
|
||||
return wstore.DBResolveEasyOID(ctx, simpleId)
|
||||
}
|
||||
|
||||
func (ws *WshServer) ResolveIdsCommand(ctx context.Context, data wshrpc.CommandResolveIdsData) (wshrpc.CommandResolveIdsRtnData, error) {
|
||||
rtn := wshrpc.CommandResolveIdsRtnData{}
|
||||
rtn.ResolvedIds = make(map[string]waveobj.ORef)
|
||||
var firstErr error
|
||||
for _, simpleId := range data.Ids {
|
||||
oref, err := resolveSimpleId(ctx, data, simpleId)
|
||||
if err != nil {
|
||||
if firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil || oref == nil {
|
||||
continue
|
||||
}
|
||||
rtn.ResolvedIds[simpleId] = *oref
|
||||
}
|
||||
if firstErr != nil && len(data.Ids) == 1 {
|
||||
return rtn, firstErr
|
||||
}
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user