Skip to content

Commit

Permalink
refactor(terminal): use golang.org/x/term
Browse files Browse the repository at this point in the history
  • Loading branch information
ken8203 committed May 7, 2023
1 parent 200aa8f commit bd13a67
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 61 deletions.
53 changes: 24 additions & 29 deletions cmd/shell.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package cmd

import (
"context"
"fmt"
"os"
"strings"

"github.com/ken8203/tikv-cli/internal/terminal"
Expand All @@ -12,57 +10,54 @@ import (

// shellRunE is the entry of shell command.
func shellRunE(cmd *cobra.Command, _ []string) error {
client, err := newClient()
if err != nil {
return fmt.Errorf("new client: %v", err)
}
defer client.Close(cmd.Context())

executeFn := func(ctx context.Context, command string, args ...string) {
switch strings.ToLower(command) {
case "put":
if err := put(ctx, args); err != nil {
fmt.Fprintln(os.Stdout, err.Error())
ctx := cmd.Context()
term := terminal.New(addr(Host, Port))

ch, errCh := term.Enter(ctx)
for command := range ch {
switch strings.ToLower(command.Cmd) {
case putCmd.Name():
if err := put(ctx, command.Args); err != nil {
fmt.Fprintln(term, err.Error())
break
}

fmt.Fprintln(os.Stdout, "OK")
fmt.Fprintln(term, "OK")
break

case "get":
value, err := get(ctx, args)
case getCmd.Name():
value, err := get(ctx, command.Args)
if err != nil {
fmt.Fprintln(os.Stdout, err.Error())
fmt.Fprintln(term, err.Error())
break
}

fmt.Fprintln(os.Stdout, string(value))
fmt.Fprintln(term, string(value))
break

case "delete":
if err := delete(ctx, args); err != nil {
fmt.Fprintln(os.Stdout, err.Error())
case deleteCmd.Name():
if err := delete(ctx, command.Args); err != nil {
fmt.Fprintln(term, err.Error())
break
}

fmt.Fprintln(os.Stdout, "OK")
fmt.Fprintln(term, "OK")
break

case "ttl":
ttl, err := ttl(ctx, args)
case ttlCmd.Name():
ttl, err := ttl(ctx, command.Args)
if err != nil {
fmt.Fprintln(os.Stdout, err.Error())
fmt.Fprintln(term, err.Error())
break
}

fmt.Fprintln(os.Stdout, ttl)
fmt.Fprintln(term, ttl)
break

default:
fmt.Fprintf(os.Stdout, "(error) ERR unknown command '%s'\n", command)
fmt.Fprintf(term, "(error) ERR unknown command '%s'\n", command)
}
}

return terminal.New(addr(Host, Port), executeFn).
Prompt(cmd.Context())
return <-errCh
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/spf13/cobra v1.7.0
github.com/tikv/client-go/v2 v2.0.7
go.uber.org/zap v1.24.0
golang.org/x/term v0.8.0
)

require (
Expand Down Expand Up @@ -46,7 +47,7 @@ require (
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.8.0 // indirect
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633 // indirect
google.golang.org/grpc v1.54.0 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,11 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
Expand Down
87 changes: 58 additions & 29 deletions internal/terminal/terminal.go
Original file line number Diff line number Diff line change
@@ -1,52 +1,81 @@
package terminal

import (
"bufio"
"context"
"errors"
"fmt"
"io"
"os"
"strings"
)

type ExecuteFn func(ctx context.Context, command string, args ...string)
"golang.org/x/term"
)

type Terminal struct {
placeholder string
executeFn ExecuteFn
*term.Terminal
}

func New(placeholder string, executeFn ExecuteFn) *Terminal {
func New(prompt string) *Terminal {
return &Terminal{
placeholder: placeholder,
executeFn: executeFn,
Terminal: term.NewTerminal(os.Stdin, prompt+"> "),
}
}

func (t *Terminal) Prompt(ctx context.Context) error {
r := bufio.NewReader(os.Stdin)
for {
fmt.Fprint(os.Stdout, t.placeholder+"> ")
s, err := r.ReadString('\n')
if err != nil {
if errors.Is(err, io.EOF) {
fmt.Fprintln(os.Stdout, "\nbye bye")
return nil
}
func (t *Terminal) Enter(ctx context.Context) (<-chan *Command, <-chan error) {
errCh := make(chan error, 1)

return err
}
state, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
errCh <- err
return nil, errCh
}

ch := make(chan *Command)
go func() {
defer term.Restore(int(os.Stdin.Fd()), state)
defer func() {
close(ch)
close(errCh)
}()

for {
select {
case <-ctx.Done():
errCh <- ctx.Err()
return
default:
line, err := t.ReadLine()
if err != nil {
if err == io.EOF {
return
}

errCh <- err
return
}
if line == "" {
continue
}

substrings := strings.Fields(s)
if len(substrings) == 0 {
continue
ch <- newCommand(line)
}
}
}()

t.executeFn(ctx, substrings[0], substrings[1:]...)
}
return ch, errCh
}

func (t *Terminal) SetPrompt(prompt string) {
t.SetPrompt(prompt + "> ")
}

func (t *Terminal) SetPlaceholder(placeholder string) {
t.placeholder = placeholder
type Command struct {
Cmd string
Args []string
}

func newCommand(line string) *Command {
substrings := strings.Fields(line)
return &Command{
Cmd: substrings[0],
Args: substrings[1:],
}
}

0 comments on commit bd13a67

Please sign in to comment.