First stab at maglevc
This commit is contained in:
94
cmd/maglevc/shell.go
Normal file
94
cmd/maglevc/shell.go
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2026, Pim van Pelt <pim@ipng.ch>
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/chzyer/readline"
|
||||
|
||||
"git.ipng.ch/ipng/vpp-maglev/internal/grpcapi"
|
||||
)
|
||||
|
||||
// errQuit is a sentinel returned by runQuit to exit the REPL.
|
||||
var errQuit = errors.New("quit")
|
||||
|
||||
// runShell runs the interactive REPL until the user types quit/exit or EOF.
|
||||
func runShell(ctx context.Context, client grpcapi.MaglevClient) error {
|
||||
root := buildTree()
|
||||
|
||||
comp := &Completer{root: root, client: client}
|
||||
ql := &questionListener{root: root, client: client}
|
||||
|
||||
cfg := &readline.Config{
|
||||
Prompt: "maglev> ",
|
||||
AutoComplete: comp,
|
||||
InterruptPrompt: "^C",
|
||||
EOFPrompt: "exit",
|
||||
Listener: ql,
|
||||
}
|
||||
rl, err := readline.NewEx(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("readline init: %w", err)
|
||||
}
|
||||
ql.rl = rl
|
||||
defer rl.Close()
|
||||
|
||||
for {
|
||||
line, err := rl.Readline()
|
||||
if err == readline.ErrInterrupt {
|
||||
continue
|
||||
}
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tokens := splitTokens(line)
|
||||
if len(tokens) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := dispatch(ctx, root, client, tokens); err != nil {
|
||||
if errors.Is(err, errQuit) {
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(rl.Stderr(), "error: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dispatch walks the tree and executes the matched command.
|
||||
func dispatch(ctx context.Context, root *Node, client grpcapi.MaglevClient, tokens []string) error {
|
||||
node, args := Walk(root, tokens)
|
||||
|
||||
if node.Run == nil {
|
||||
showHelp(root, tokens)
|
||||
return nil
|
||||
}
|
||||
|
||||
return node.Run(ctx, client, args)
|
||||
}
|
||||
|
||||
// showHelp prints available completions for the given token path.
|
||||
func showHelp(root *Node, tokens []string) {
|
||||
node, _ := Walk(root, tokens)
|
||||
|
||||
candidates := filterFixedChildren(node.Children, "")
|
||||
if len(candidates) == 0 {
|
||||
fmt.Println(" <no completions>")
|
||||
return
|
||||
}
|
||||
for _, c := range candidates {
|
||||
if c.Help != "" {
|
||||
fmt.Printf(" %-20s %s\n", c.Word, c.Help)
|
||||
} else {
|
||||
fmt.Printf(" %s\n", c.Word)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user