76fbe2eee0
Replace maglevc's hand-rolled command-tree CLI with git.ipng.ch/ipng/golang-cli v1.3.0, mirroring the evpnc refactor. The tree (commands.go) and the gRPC-status error unwrap (color.go) stay app-specific; the generic parts — parse tree, completion, '?'-help, the readline shell, one-shot dispatch, color helpers, and the watch keypress handler — now come from the library. - main.go: a single cli.App[grpcapi.MaglevClient] with a Connect callback; drops the flag/color-default/dispatch boilerplate. - commands.go: `type node = cli.Node[grpcapi.MaglevClient]`; label() -> cli.Label(); dyn* gain the captured-args parameter the library's Dynamic signature carries; runQuit returns cli.ErrQuit. - watch.go: keypress.WaitForKey replaces the inline cbreak helper. - color.go: only formatError remains, reading cli.ColorEnabled(). - delete tree.go, complete.go, shell.go. - tests use the library API; add TestTreeValid (cli.Validate). Behavior is unchanged except labels/errors now use the library's bright ANSI palette (was dark); escape lengths are identical so tabwriter alignment is unaffected. maglevc additionally gains the OpenBSD readline fix and BSD-correct watch keypress it previously lacked. Builds on linux and openbsd. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
1.9 KiB
Go
54 lines
1.9 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Command maglevc is the vpp-maglev CLI. It talks only to maglevd. With no
|
|
// arguments it starts an interactive shell (readline, tab-completion, '?' help,
|
|
// prefix abbreviation); with arguments it runs one command and exits. The
|
|
// command set is a single declarative tree (buildTree) -- dispatch, help, and
|
|
// completion are all derived from it, via the git.ipng.ch/ipng/golang-cli
|
|
// library.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
|
|
cli "git.ipng.ch/ipng/golang-cli"
|
|
buildinfo "git.ipng.ch/ipng/vpp-maglev/cmd"
|
|
"git.ipng.ch/ipng/vpp-maglev/internal/grpcapi"
|
|
"git.ipng.ch/ipng/vpp-maglev/internal/netutil"
|
|
)
|
|
|
|
// defaultGRPCPort is the maglevd gRPC port (mirrors the server's -grpc-addr
|
|
// default), used when -server is given without an explicit ":<port>".
|
|
const defaultGRPCPort = "9090"
|
|
|
|
func main() {
|
|
(&cli.App[grpcapi.MaglevClient]{
|
|
Name: "maglevc",
|
|
Version: buildinfo.Version(),
|
|
Commit: buildinfo.Commit(),
|
|
Date: buildinfo.Date(),
|
|
Prompt: "maglev> ",
|
|
Root: buildTree(),
|
|
DefaultServer: "localhost:9090",
|
|
ServerEnv: "MAGLEV_SERVER",
|
|
Connect: connect,
|
|
FormatError: formatError,
|
|
}).Main()
|
|
}
|
|
|
|
// connect dials maglevd and returns the gRPC client. App resolves -server (env
|
|
// MAGLEV_SERVER, default localhost:9090) and hands us the raw address; we ensure
|
|
// a port and dial insecure, mirroring maglevd's default transport.
|
|
func connect(_ context.Context, server string) (grpcapi.MaglevClient, func(), error) {
|
|
addr := netutil.EnsurePort(server, defaultGRPCPort)
|
|
conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("connect %s: %w", addr, err)
|
|
}
|
|
return grpcapi.NewMaglevClient(conn), func() { _ = conn.Close() }, nil
|
|
}
|