-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from baibikov/cli
Cli
- Loading branch information
Showing
27 changed files
with
701 additions
and
176 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.