Skip to content

Commit

Permalink
enhance: Abstract state into common pkg
Browse files Browse the repository at this point in the history
This PR

- Abstract state interface into common pkg
- move birdwatcher main.go to cmd pkg following convention

Signed-off-by: Congqi Xia <[email protected]>
  • Loading branch information
congqixia committed Dec 24, 2024
1 parent 0c6b1a9 commit d9d5bda
Show file tree
Hide file tree
Showing 27 changed files with 267 additions and 241 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ all: static-check birdwatcher
birdwatcher:
@echo "Compiling birdwatcher"
@mkdir -p bin
@CGO_ENABLED=0 go build -o bin/birdwatcher main.go
@CGO_ENABLED=0 go build -o bin/birdwatcher cmd/birdwatcher/main.go

birdwatcher_wkafka:
@echo "Compiling birdwatcher with kafka(CGO_ENABLED)"
@mkdir -p bin
@CGO_ENABLED=1 go build -o bin/birdwatcher_wkafka -tags WKAFKA main.go
@CGO_ENABLED=1 go build -o bin/birdwatcher_wkafka -tags WKAFKA cmd/birdwatcher/main.go

getdeps:
@mkdir -p $(INSTALL_PATH)
Expand Down
4 changes: 2 additions & 2 deletions bapps/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package bapps
import (
"log"

"github.com/milvus-io/birdwatcher/states"
"github.com/milvus-io/birdwatcher/common"
)

// BApp interface for birdwatcher application
type BApp interface {
Run(states.State)
Run(common.State)
}

// AppOption application setup option function.
Expand Down
6 changes: 3 additions & 3 deletions bapps/go_prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import (
"github.com/c-bata/go-prompt"
"github.com/samber/lo"

"github.com/milvus-io/birdwatcher/common"
"github.com/milvus-io/birdwatcher/configs"
"github.com/milvus-io/birdwatcher/history"
"github.com/milvus-io/birdwatcher/states"
)

// PromptApp wraps go-prompt as application.
type PromptApp struct {
exited bool
currentState states.State
currentState common.State
sugguestHistory bool
historyHelper *history.Helper
logger *log.Logger
Expand Down Expand Up @@ -71,7 +71,7 @@ func NewPromptApp(config *configs.Config, opts ...AppOption) BApp {
return pa
}

func (a *PromptApp) Run(start states.State) {
func (a *PromptApp) Run(start common.State) {
a.currentState = start
a.prompt.Run()
}
Expand Down
5 changes: 2 additions & 3 deletions bapps/olc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import (
"os"
"strings"

"github.com/milvus-io/birdwatcher/common"
"github.com/samber/lo"

"github.com/milvus-io/birdwatcher/states"
)

type olcApp struct {
Expand All @@ -25,7 +24,7 @@ func NewOlcApp(script string) BApp {
}
}

func (a *olcApp) Run(start states.State) {
func (a *olcApp) Run(start common.State) {
app := start
cmds := a.parseScripts(a.script)
var err error
Expand Down
8 changes: 4 additions & 4 deletions bapps/promptui.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ import (

"github.com/manifoldco/promptui"

"github.com/milvus-io/birdwatcher/states"
"github.com/milvus-io/birdwatcher/common"
)

// simpleApp wraps promptui as BApp.
type simpleApp struct {
currentState states.State
currentState common.State
}

func NewSimpleApp() BApp {
return &simpleApp{}
}

// Run starts BirdWatcher with promptui. (disable suggestion and history)
func (a *simpleApp) Run(start states.State) {
func (a *simpleApp) Run(start common.State) {
app := start
for {
p := promptui.Prompt{
Expand All @@ -31,7 +31,7 @@ func (a *simpleApp) Run(start states.State) {
line, err := p.Run()
if err == nil {
app, err = app.Process(line)
if errors.Is(err, states.ExitErr) {
if errors.Is(err, common.ExitErr) {
break
}
if app.IsEnding() {
Expand Down
8 changes: 4 additions & 4 deletions bapps/webserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type InstanceInfo struct {
RootPath string `form:"rootPath"`
}

func (app *WebServerApp) Run(states.State) {
func (app *WebServerApp) Run(common.State) {
r := gin.Default()
etcdversion.SetVersion(models.GTEVersion2_2)

Expand All @@ -42,7 +42,7 @@ func (app *WebServerApp) Run(states.State) {
r.Run(fmt.Sprintf(":%d", app.port))
}

func (app *WebServerApp) ParseRouter(r *gin.Engine, s states.State) {
func (app *WebServerApp) ParseRouter(r *gin.Engine, s common.State) {
v := reflect.ValueOf(s)
tp := v.Type()

Expand Down Expand Up @@ -106,7 +106,7 @@ func (app *WebServerApp) parseMethod(r *gin.Engine, mt reflect.Method, name stri

// fmt.Println(mt.Name)
cp := reflect.New(paramType.Elem()).Interface().(framework.CmdParam)
fUse, _ := states.GetCmdFromFlag(cp)
fUse, _ := common.GetCmdFromFlag(cp)
if len(use) == 0 {
use = fUse
}
Expand All @@ -115,7 +115,7 @@ func (app *WebServerApp) parseMethod(r *gin.Engine, mt reflect.Method, name stri
fnName := mt.Name
use = strings.ToLower(fnName[:len(fnName)-8])
}
uses := states.ParseUseSegments(use)
uses := common.ParseUseSegments(use)
lastKw := uses[len(uses)-1]
// hard code, show xxx command only
if uses[0] != "show" {
Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions common/exit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package common

// ExitState simple exit state.
type ExitState struct {
CmdState
}

// SetupCommands setups the command.
// also called after each command run to reset flag values.
func (s *ExitState) SetupCommands() {}

// IsEnding returns true for exit State
func (s *ExitState) IsEnding() bool { return true }
86 changes: 54 additions & 32 deletions states/states.go → common/state.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package states
package common

import (
"context"
Expand All @@ -18,6 +18,17 @@ import (
"github.com/milvus-io/birdwatcher/states/autocomplete"
)

// ExitErr is the error indicates user needs to exit application.
var ExitErr = exitErr{}

// exitErr internal err type for comparing.
type exitErr struct{}

// Error implements error.
func (e exitErr) Error() string {
return "exited"
}

// State is the interface for application state.
type State interface {
Ctx() (context.Context, context.CancelFunc)
Expand All @@ -30,18 +41,18 @@ type State interface {
IsEnding() bool
}

// cmdState is the basic state to process input command.
type cmdState struct {
label string
rootCmd *cobra.Command
nextState State
// CmdState is the basic state to process input command.
type CmdState struct {
LabelStr string
RootCmd *cobra.Command
NextState State
signal <-chan os.Signal

setupFn func()
SetupFn func()
}

// Ctx returns context which bind to sigint handler.
func (s *cmdState) Ctx() (context.Context, context.CancelFunc) {
func (s *CmdState) Ctx() (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(context.Background())
go func() {
defer cancel()
Expand All @@ -54,44 +65,47 @@ func (s *cmdState) Ctx() (context.Context, context.CancelFunc) {
}

// SetupCommands perform command setup & reset.
func (s *cmdState) SetupCommands() {
if s.setupFn != nil {
s.setupFn()
func (s *CmdState) SetupCommands() {
if s.SetupFn != nil {
s.SetupFn()
}
}

// mergeFunctionCommands parses all member methods for provided state and add it into cmd.
func (s *cmdState) mergeFunctionCommands(cmd *cobra.Command, state State) {
// MergeFunctionCommands parses all member methods for provided state and add it into cmd.
func (s *CmdState) MergeFunctionCommands(cmd *cobra.Command, state State) {
items := parseFunctionCommands(state)
for _, item := range items {
// log.Println(item.kws, item.cmd.Use)
target := cmd
for _, kw := range item.kws {
node, _, err := cmd.Find([]string{kw})
if err != nil {
if err != nil || node.Use == "" {
// log.Println("not found", err)
newNode := &cobra.Command{Use: kw}
target.AddCommand(newNode)
node = newNode
}
// log.Println("[After find&check]", node.Use)
target = node
}
target.AddCommand(item.cmd)
}
}

// Label returns the display label for current cli.
func (s *cmdState) Label() string {
return s.label
func (s *CmdState) Label() string {
return s.LabelStr
}

func (s *cmdState) Suggestions(input string) map[string]string {
return autocomplete.SuggestInputCommands(input, s.rootCmd.Commands())
func (s *CmdState) Suggestions(input string) map[string]string {
return autocomplete.SuggestInputCommands(input, s.RootCmd.Commands())
}

// Process is the main entry for processing command.
func (s *cmdState) Process(cmd string) (State, error) {
func (s *CmdState) Process(cmd string) (State, error) {
args := strings.Split(cmd, " ")

target, _, err := s.rootCmd.Find(args)
target, _, err := s.RootCmd.Find(args)
if err == nil && target != nil {
defer target.SetArgs(nil)
}
Expand All @@ -101,18 +115,18 @@ func (s *cmdState) Process(cmd string) (State, error) {
signal.Notify(c, syscall.SIGINT)
s.signal = c

s.rootCmd.SetArgs(args)
err = s.rootCmd.Execute()
s.RootCmd.SetArgs(args)
err = s.RootCmd.Execute()
signal.Reset(syscall.SIGINT)
if errors.Is(err, ExitErr) {
return s.nextState, ExitErr
return s.NextState, ExitErr
}
if err != nil {
return s, err
}
if s.nextState != nil {
nextState := s.nextState
s.nextState = nil
if s.NextState != nil {
nextState := s.NextState
s.NextState = nil
return nextState, nil
}

Expand All @@ -122,23 +136,31 @@ func (s *cmdState) Process(cmd string) (State, error) {
}

// SetNext simple method to set next state.
func (s *cmdState) SetNext(state State) {
s.nextState = state
func (s *CmdState) SetNext(state State) {
s.NextState = state
}

// Close empty method to implement State.
func (s *cmdState) Close() {}
func (s *CmdState) Close() {}

// Check state is ending state.
func (s *cmdState) IsEnding() bool { return false }
func (s *CmdState) IsEnding() bool { return false }

type PrintVerParam struct {
framework.ParamBase `use:"version" desc:"print version"`
}

func (s *CmdState) PrintVersionCommand(ctx context.Context, _ *PrintVerParam) {
fmt.Println("Birdwatcher Version", Version)
}

type exitParam struct {
framework.ParamBase `use:"exit" desc:"Close this CLI tool"`
}

// ExitCommand returns exit command
func (s *cmdState) ExitCommand(ctx context.Context, _ *exitParam) {
s.SetNext(&exitState{})
func (s *CmdState) ExitCommand(ctx context.Context, _ *exitParam) {
s.SetNext(&ExitState{})
}

type commandItem struct {
Expand Down
49 changes: 49 additions & 0 deletions common/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package common

import (
"context"
"fmt"
"strconv"
"time"

"github.com/milvus-io/birdwatcher/framework"
)

const (
logicalBits = 18
logicalBitsMask = (1 << logicalBits) - 1
)

type ParseTSParam struct {
framework.ParamBase `use:"parse-ts" desc:"parse hybrid timestamp"`
args []string
}

func (p *ParseTSParam) ParseArgs(args []string) error {
p.args = args
return nil
}

func (s *CmdState) ParseTSCommand(ctx context.Context, p *ParseTSParam) {
if len(p.args) == 0 {
fmt.Println("no ts provided")
}

for _, arg := range p.args {
ts, err := strconv.ParseUint(arg, 10, 64)
if err != nil {
fmt.Printf("failed to parse ts from %s, err: %s\n", arg, err.Error())
continue
}

t, _ := ParseTS(ts)
fmt.Printf("Parse ts result, ts:%d, time: %v\n", ts, t)
}
}

func ParseTS(ts uint64) (time.Time, uint64) {
logical := ts & logicalBitsMask
physical := ts >> logicalBits
physicalTime := time.Unix(int64(physical/1000), int64(physical)%1000*time.Millisecond.Nanoseconds())
return physicalTime, logical
}
Loading

0 comments on commit d9d5bda

Please sign in to comment.