Skip to content

Commit

Permalink
feat: added autocompleter and command history using `github.com/peter…
Browse files Browse the repository at this point in the history
…h/liner` package
  • Loading branch information
HotPotatoC committed Apr 14, 2021
1 parent 5e42800 commit ce6a719
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 35 deletions.
78 changes: 48 additions & 30 deletions cli/cli.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package cli

import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"syscall"

Expand All @@ -16,12 +15,13 @@ import (
"github.com/HotPotatoC/kvstore/pkg/comm"
"github.com/HotPotatoC/kvstore/pkg/utils"
"github.com/HotPotatoC/kvstore/server/stats"
"github.com/peterh/liner"
)

// CLI represents the cli client
type CLI struct {
comm *comm.Comm
reader *bufio.Reader
comm *comm.Comm
terminal *liner.State
}

// New creates a new CLI client
Expand All @@ -32,46 +32,38 @@ func New(addr string) *CLI {
}

return &CLI{
comm: comm,
reader: bufio.NewReader(os.Stdin),
comm: comm,
terminal: newTerminal(),
}
}

// Start runs the CLI client
func (c *CLI) Start() {
defer c.terminal.Close()
go func() {
// Get server information on initial startup
stats := c.getServerInformation()

logo := "" +
" _ _ _ _\n" +
"| | | | | (_)\n" +
"| | ____ _____| |_ ___ _ __ ___ ______ ___| |_\n" +
"| |/ /\\ \\ / / __| __/ _ \\| '__/ _ \\______/ __| | |\n" +
"| < \\ V /\\__ \\ || (_) | | | __/ | (__| | |\n" +
"|_|\\_\\ \\_/ |___/\\__\\___/|_| \\___| \\___|_|_|\n\n"

fmt.Println(logo)
fmt.Printf("Connected to kvstore %s:%s server!\n\n", stats.Version, stats.Build)
c.printLogo()
fmt.Printf("🚀 Connected to kvstore %s:%s server!\n\n", stats.Version, stats.Build)
start:
for {
fmt.Printf("%s> ", c.comm.Connection().RemoteAddr().String())

input, err := c.reader.ReadBytes('\n')
if err != nil && err != io.EOF {
input, err := c.terminal.Prompt(fmt.Sprintf("%s> ", c.comm.Connection().RemoteAddr().String()))
if err != nil {
if err == io.EOF {
c.comm.Conn.Close()
os.Exit(1)
}
log.Fatal(err)
}

raw := bytes.Split(input, []byte(" "))[0]
cmd := bytes.ToLower(
bytes.TrimSpace(raw))
args := bytes.TrimSpace(
bytes.TrimPrefix(input, raw))
c.terminal.AppendHistory(input)
cmd, args := c.parseCommand(input)

switch string(cmd) {
switch cmd {
// Displays all available commands with their args and description
case "help":
var commands = []command.Op{
commands := []command.Op{
command.SET,
command.SETEX,
command.GET,
Expand All @@ -89,7 +81,6 @@ func (c *CLI) Start() {
dimmed(cmd.Args()),
cmd.Description())
}
continue start
// Exit out of the CLI
case "exit":
c.comm.Conn.Close()
Expand All @@ -107,13 +98,21 @@ func (c *CLI) Start() {
log.Fatal(err)
}

msg, _, err := c.comm.Read()
msg, n, err := c.comm.Read()
if err != nil && err != io.EOF {
log.Fatal(err)
}

fmt.Print(string(msg))
fmt.Print(string(msg[:n]))
}

// Write history into tmp direcotry
f, err := os.Create(filepath.Join(os.TempDir(), ".kvstore-cli-history"))
if err != nil {
log.Printf("Failed creating history file %s\n", filepath.Join(os.TempDir(), ".kvstore-cli-history"))
}
c.terminal.WriteHistory(f)
_ = f.Close()
}
}()

Expand All @@ -122,6 +121,15 @@ func (c *CLI) Start() {
os.Exit(0)
}

func (c *CLI) printLogo() {
fmt.Println(" _ _ _ _\n" +
"| | | | | (_)\n" +
"| | ____ _____| |_ ___ _ __ ___ ______ ___| |_\n" +
"| |/ /\\ \\ / / __| __/ _ \\| '__/ _ \\______/ __| | |\n" +
"| < \\ V /\\__ \\ || (_) | | | __/ | (__| | |\n" +
"|_|\\_\\ \\_/ |___/\\__\\___/|_| \\___| \\___|_|_|\n\n")
}

func (c *CLI) getServerInformation() *stats.Stats {
serverStats := new(stats.Stats)
infoPacket := packet.NewPacket(command.INFO, []byte(""))
Expand All @@ -147,3 +155,13 @@ func (c *CLI) getServerInformation() *stats.Stats {

return serverStats
}

func (c *CLI) parseCommand(input string) (string, string) {
raw := strings.Fields(input)[0]
cmd := strings.ToLower(
strings.TrimSpace(raw))
args := strings.TrimSpace(
strings.TrimPrefix(input, raw))

return cmd, args
}
10 changes: 5 additions & 5 deletions cli/preprocess_command.go → cli/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ import (
"github.com/HotPotatoC/kvstore/packet"
)

func preprocess(cmd, args []byte) (*bytes.Buffer, error) {
func preprocess(cmd string, args string) (*bytes.Buffer, error) {
var packet *packet.Packet
var err error

switch string(cmd) {
case command.SET.String():
if packet, err = set(args); err != nil {
if packet, err = set([]byte(args)); err != nil {
return nil, err
}
case command.SETEX.String():
if packet, err = setex(args); err != nil {
if packet, err = setex([]byte(args)); err != nil {
return nil, err
}
case command.GET.String():
if packet, err = get(args); err != nil {
if packet, err = get([]byte(args)); err != nil {
return nil, err
}
case command.DEL.String():
if packet, err = del(args); err != nil {
if packet, err = del([]byte(args)); err != nil {
return nil, err
}
case command.LIST.String():
Expand Down
41 changes: 41 additions & 0 deletions cli/terminal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package cli

import (
"os"
"path/filepath"
"strings"

"github.com/HotPotatoC/kvstore/command"
"github.com/peterh/liner"
)

func newTerminal() *liner.State {
liner := liner.NewLiner()

liner.SetCtrlCAborts(true)
liner.SetCompleter(func(s string) (c []string) {
commands := []command.Op{
command.SET,
command.SETEX,
command.GET,
command.DEL,
command.LIST,
command.KEYS,
command.FLUSH,
command.INFO,
}
for _, n := range commands {
if strings.HasPrefix(n.String(), strings.ToLower(s)) {
c = append(c, n.String())
}
}
return
})

if f, err := os.Open(filepath.Join(os.TempDir(), ".kvstore-cli-history")); err == nil {
liner.ReadHistory(f)
f.Close()
}

return liner
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ go 1.15
require (
github.com/cespare/xxhash/v2 v2.1.1
github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-runewidth v0.0.12 // indirect
github.com/peterh/liner v1.2.1
github.com/rivo/uniseg v0.2.0 // indirect
go.uber.org/zap v1.16.0
golang.org/x/sys v0.0.0-20210414055047-fe65e336abe0 // indirect
)
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,18 @@ github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/peterh/liner v1.2.1 h1:O4BlKaq/LWu6VRWmol4ByWfzx6MfXc5Op5HETyIy5yg=
github.com/peterh/liner v1.2.1/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
Expand Down

0 comments on commit ce6a719

Please sign in to comment.