8000 Merge pull request #2 from baibikov/cli · baibikov/jellydb@3cad5b2 · GitHub
[go: up one dir, main page]

Skip to content

Commit

Permalink
Merge pull request #2 from baibikov/cli
Browse files Browse the repository at this point in the history
Cli
  • Loading branch information
baibikov authored Sep 13, 2022
2 parents 2cd4032 + 14dc749 commit 3cad5b2
Show file tree
Hide file tree
Showing 27 changed files with 701 additions and 176 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pkg/jellystore/test_path/*
.idea/*
internal/pkg/jellystore/test_path/*
.idea/*
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
proto-gen:
protoc --go_out=. --go-grpc_out=. ./api/proto/*.proto
protoc --go_out=. --go-grpc_out=. ./api/proto/*.proto
63 changes: 63 additions & 0 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"context"
"flag"
"log"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"go.uber.org/multierr"

"github.com/baibikov/jellydb/internal/cli"
"github.com/baibikov/jellydb/pkg/notifyctx"
)

func main() {
flags, err := parse()
if err != nil {
logrus.Error(err)
return
}

if err := runApp(flags); err != nil {
log.Fatalln(err)
}
}

type Flags struct {
addr string
}

func parse() (*Flags, error) {
var addr string
flag.StringVar(&addr, "addr", "", "an init")

flag.Parse()
if addr == "" {
return nil, errors.New("addr is required param")
}
return &Flags{
addr: addr,
}, nil
}

func runApp(f *Flags) (err error) {
ctx := context.Background()
ctx, cancel := notifyctx.WrapExitContext(ctx)
defer cancel()

c, err := cli.New(&cli.Config{
Addr: f.addr,
})
if err != nil {
return err
}
defer multierr.AppendInvoke(&err, multierr.Close(c))

go func() {
c.Run(ctx)
}()
<-ctx.Done()
return nil
}
6 changes: 5 additions & 1 deletion cmd/tcp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ import (
"github.com/sirupsen/logrus"
"go.uber.org/multierr"

"github.com/baibikov/jellydb/internal/pkg/jellystore"
"github.com/baibikov/jellydb/internal/tcp"
"github.com/baibikov/jellydb/pkg/jellystore"
)

func init() {
logrus.SetLevel(logrus.DebugLevel)
}

func main() {
flags, err := parse()
if err != nil {
Expand Down
165 changes: 165 additions & 0 deletions internal/cli/cli.go
8000
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package cli

import (
"bufio"
"context"
"fmt"
"net"
"os"
"strings"

"github.com/pkg/errors"
)

type Cli struct {
conn net.Conn
}

func (c Cli) Close() error {
return c.conn.Close()
}

type Config struct {
Addr string
}

func New(config *Config) (*Cli, error) {
if config == nil {
return nil, errors.New("config has not be empty")
}

if config.Addr == "" {
return nil, errors.New("config addr has not be empty")
}

conn, err := net.Dial("tcp", config.Addr)
if err != nil {
return nil, errors.Wrapf(err, "connect by tcp protocol to address %s", config.Addr)
}

return &Cli{conn: conn}, nil
}

const foreword = `JellyDB (message broker database) СLI 🤟
----------------------
(sys)
-help: Navigating existing Commands
exit: Exit from CLI
clear: Carriage cleaning
`

const commands = `commands:
(sys)
-help: Navigating existing Commands
exit: Exit from CLI
clear: Carriage cleaning
(store)
SET: Adding an entry to the read queue, as soon as the entry
example:
> SET my_super_important SOME_VALUE_1
GET [N]: Getting uncommitted messages from the batch queue and n is batch elements
example:
> GET my_super_important 2
> SOME_VALUE_1
> SOME_VALUE_2
COM [N]: Commenting on a batch of messages
example:
> COMMIT my_super_important 2
S_ERR: syntax error, displayed if you made a mistake while writing the request
E_ERR: system error, the error indicates that you encountered a problem while executing the request
`

const (
helpCommand = "-help"
exitCommand = "exit"
clearCommand = "clear"
)

const (
setCommand = "SET"
getCommand = "GET"
commitCommand = "COM"
)

const (
undefinedCommand = "undefined command"
escapeInfo = "\033[H\033[2J"
lineBreak = "\n"
)

func (c *Cli) Run(ctx context.Context) {
fmt.Print(foreword, lineBreak)
reader := bufio.NewReader(os.Stdin)

cliLoop:
for {
select {
case <-ctx.Done():
return
default:
}
fmt.Print("> ")
text, _ := reader.ReadString('\n')

tape := strings.Trim(strings.TrimSpace(text), lineBreak)

switch tape {
case helpCommand:
fmt.Print(commands, lineBreak)
case exitCommand:
fmt.Println("👋 buy")
break cliLoop
case clearCommand:
fmt.Print(escapeInfo, lineBreak)
case "":
continue
default:
tree := commandTree(tape)
if len(tree) == 0 {
fmt.Printf(`💨 %s "%s"%s`, undefinedCommand, tape, lineBreak)
continue
}

payload, err := c.execCommand(tree)
if err != nil {
fmt.Printf("🚫 %v %s", err, lineBreak)
continue
}

for _, pp := range payload {
fmt.Println(pp)
}
}
}
}

const (
commandIndex = 0
)

func commandTree(tape string) []string {
sep := strings.Split(tape, " ")
if len(sep) == 0 {
return nil
}

com := sep[commandIndex]
if !isStoreCommand(com) {
return nil
}

res := append([]string(nil), com)
return append(res, sep[1:]...)
}

func (c *Cli) execCommand(tree []string) ([]string, error) {
return newCommand(tree[0], c.conn, tree[1:])
}

func isStoreCommand(s string) bool {
return s == setCommand || s == getCommand || s == commitCommand
}
56 changes: 56 additions & 0 deletions internal/cli/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package cli

import (
"net"

"github.com/pkg/errors"
)

var (
ErrNoParams = errors.New("command params is empty")
ErrNoAllowedParams = errors.New("the number of parameters exceeds the allowable")
)

type commander interface {
validate(params []string) error
ping() error
exec() error
payload() []string
}

func newCommand(typ string, conn net.Conn, params []string) (p []string, err error) {
var cc commander
switch typ {
case setCommand:
cc = &settcommand{
conn: conn,
}
case getCommand:
cc = &getcommand{
conn: conn,
}
case commitCommand:
cc = &commitcommand{
conn: conn,
}
default:
return nil, errors.New("S_ERR: undefined command")
}

err = cc.validate(params)
if err != nil {
return nil, errors.Wrap(err, "S_ERR")
}

err = cc.ping()
if err != nil {
return nil, errors.Wrap(err, "E_ERR")
}

err = cc.exec()
if err != nil {
return nil, errors.Wrap(err, "E_ERR")
}

return cc.payload(), nil
}
62 changes: 62 additions & 0 deletions internal/cli/commitcommand.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package cli

import (
"net"
"strconv"

"github.com/pkg/errors"

"github.com/baibikov/jellydb/pkg/protomarshal"
"github.com/baibikov/jellydb/protogenerated/messages"
)

type commitcommand struct {
conn net.Conn

key string
n int64
}

func (c *commitcommand) validate(params []string) (err error) {
if len(params) == 0 {
return ErrNoParams
}
if len(params) != 2 {
return ErrNoAllowedParams
}

c.key = params[nIndex]
c.n, err = strconv.ParseInt(params[nIndex], 10, 64)
if err != nil {
return errors.Errorf("%s is not int64", params[nIndex])
}

return nil
}

func (c *commitcommand) exec() error {
mm := &messages.CommitRequest{
Key: c.key,
N: c.n,
}
err := protomarshal.NewDecoder(c.conn).Decode(mm)
if err != nil {
return errors.Wrapf(err, "%s command exec", commitCommand)
}

err = readResponse(c.conn)
if err != nil {
return err
}

return nil
}

func (c *commitcommand) payload() []string {
return []string{"👌"}
}

func (c *commitcommand) ping() error {
_, err := c.conn.Write([]byte("3"))
return errors.Wrapf(err, "%s ping the tcp server", commitCommand)
}
Loading

0 comments on commit 3cad5b2

Please sign in to comment.
0