Skip to content

Commit

Permalink
✨ feat: event support prefix hooks(eg: app.*), add new event: OnAppExit
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Jul 19, 2023
1 parent ef8e59b commit 4566d6a
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 19 deletions.
18 changes: 10 additions & 8 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/gookit/goutil/cflag"
"github.com/gookit/goutil/cliutil"
"github.com/gookit/goutil/errorx"
"github.com/gookit/goutil/maputil"
)

/*************************************************************
Expand Down Expand Up @@ -281,7 +282,7 @@ func (app *App) doParseOpts(args []string) error {

// parseAppOpts parse global options
func (app *App) parseAppOpts(args []string) (ok bool) {
Logf(VerbDebug, "will begin parse global options")
Logf(VerbDebug, "will begin parse app options, input-args: %v", args)

// parse global options
if err := app.doParseOpts(args); err != nil { // has error.
Expand Down Expand Up @@ -314,7 +315,7 @@ func (app *App) parseAppOpts(args []string) (ok bool) {
color.Enable = false
}

Debugf("app options parsed, Verbose level: <mgb>%s</>", app.opts.Verbose.String())
Debugf("app options parsed, verbose: <mgb>%s</>, options: %#v", app.opts.Verbose.String(), app.opts)

// TODO show auto-completion for bash/zsh
if app.opts.inCompletion {
Expand Down Expand Up @@ -474,7 +475,7 @@ func (app *App) Run(args []string) (code int) {
args = os.Args[1:] // exclude first arg, it's binFile.
}

Debugf("will begin run application. args: %v", args)
Debugf("will begin run application. input-args: %v", args)

// parse global flags
if false == app.parseAppOpts(args) {
Expand Down Expand Up @@ -524,7 +525,6 @@ func (app *App) RunCmd(name string, args []string) error {
if !app.HasCommand(name) {
return errorx.Failf(ERR, "command %q not exists", name)
}

return app.doRunCmd(name, args)
}

Expand All @@ -538,7 +538,7 @@ func (app *App) doRunCmd(name string, args []string) (err error) {
err = newRunErr(ERR, err)
app.Fire(events.OnAppRunError, map[string]any{"err": err})
} else {
app.Fire(events.OnAppRunAfter, nil)
app.Fire(events.OnAppRunAfter, map[string]any{"cmd": name})
}
return
}
Expand Down Expand Up @@ -582,7 +582,7 @@ func (app *App) Exec(path string, args []string) error {
* helper methods
*************************************************************/

// Opts get
// Opts get the app GlobalOpts
func (app *App) Opts() *GlobalOpts {
return app.opts
}
Expand All @@ -602,6 +602,8 @@ func (app *App) Exit(code int) {

func (app *App) exitOnEnd(code int) int {
Debugf("application exit with code: %d", code)
app.Fire(events.OnAppExit, map[string]any{"code": code})

// if IsGteVerbose(VerbDebug) {
// app.Infoln("[DEBUG] The Runtime Call Stacks:")

Expand Down Expand Up @@ -635,15 +637,15 @@ func (app *App) On(name string, handler HookFunc) {

// fire hook on the app. returns True for stop continue run.
func (app *App) fireWithCmd(event string, cmd *Command, data map[string]any) bool {
Debugf("trigger the application event: <green>%s</>", event)
Debugf("trigger the application event: <green>%s</>, data: %s", event, maputil.ToString(data))

ctx := newHookCtx(event, cmd, data).WithApp(app)
return app.Hooks.Fire(event, ctx)
}

// Fire hook on the app. returns True for stop continue run.
func (app *App) Fire(event string, data map[string]any) bool {
Debugf("trigger the application event: <green>%s</>", event)
Debugf("trigger the application event: <green>%s</>, data: %s", event, maputil.ToString(data))

ctx := newHookCtx(event, nil, data).WithApp(app)
return app.Hooks.Fire(event, ctx)
Expand Down
13 changes: 9 additions & 4 deletions events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ const (
OnAppInitBefore = "app.init.before"
// OnAppInitAfter On app init after
OnAppInitAfter = "app.init.after"
// OnAppStop = "app.stopped"
// OnAppExit On app exit before
OnAppExit = "app.exit"

// OnAppBindOptsBefore bind app options
// OnAppBindOptsBefore before bind app options
OnAppBindOptsBefore = "app.bind.opts.before"
OnAppBindOptsAfter = "app.bind.opts.after"
// OnAppBindOptsAfter after bind app options.
//
// support binding custom global options
OnAppBindOptsAfter = "app.bind.opts.after"

// OnAppCmdAdd on app cmd add
OnAppCmdAdd = "app.cmd.add.before"
Expand All @@ -24,9 +28,10 @@ const (
// {args: app-args}
OnAppOptsParsed = "app.opts.parsed"

// OnAppPrepared prepare for run
// OnAppPrepared prepare for run, after the OnAppOptsParsed
OnAppPrepared = "app.run.prepared"

// OnAppRunBefore app run before, after the OnAppPrepared
OnAppRunBefore = "app.run.before"
OnAppRunAfter = "app.run.after"
OnAppRunError = "app.run.error"
Expand Down
35 changes: 33 additions & 2 deletions ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ type HookFunc func(ctx *HookCtx) (stop bool)

// Hooks struct. hookManager
type Hooks struct {
// pfxHooks can set prefix match hooks func on running.
// eg: app.run.* => app.run.init, app.run.after
pfxHooks map[string]HookFunc
// Hooks can set some hooks func on running.
hooks map[string]HookFunc
}
Expand All @@ -88,6 +91,15 @@ func (h *Hooks) On(name string, handler HookFunc) {
panicf("event %q handler is nil", name)
}

if strings.HasSuffix(name, ".*") {
if h.pfxHooks == nil {
h.pfxHooks = make(map[string]HookFunc)
}

h.pfxHooks[name[:len(name)-2]] = handler
return
}

if h.hooks == nil {
h.hooks = make(map[string]HookFunc)
}
Expand All @@ -104,8 +116,26 @@ func (h *Hooks) AddHook(name string, handler HookFunc) {
// Fire event by name, allow with event data.
// returns True for stop continue run.
func (h *Hooks) Fire(event string, ctx *HookCtx) (stop bool) {
if handler, ok := h.hooks[event]; ok {
return handler(ctx)
if fn, ok := h.hooks[event]; ok {
if fn(ctx) {
return true
}
}

// check prefix match hooks
for name, fn := range h.pfxHooks {
if strings.HasPrefix(event, name) {
if fn(ctx) {
return true
}
}
}

// check * hook
if fn, ok := h.hooks["*"]; ok {
if fn(ctx) {
return true
}
}
return false
}
Expand All @@ -119,6 +149,7 @@ func (h *Hooks) HasHook(event string) bool {
// ResetHooks clear all hooks
func (h *Hooks) ResetHooks() {
h.hooks = nil
h.pfxHooks = nil
}

/*************************************************************
Expand Down
33 changes: 33 additions & 0 deletions ext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/gookit/gcli/v3"
"github.com/gookit/goutil/byteutil"
"github.com/gookit/goutil/testutil/assert"
)

Expand All @@ -30,3 +31,35 @@ func TestHelpReplacer(t *testing.T) {
// invalid input
is.Eq("hello {key0}", vs.ReplacePairs("hello {key0}"))
}

func TestHooks_Fire(t *testing.T) {
is := assert.New(t)
buf := byteutil.NewBuffer()
hooks := gcli.Hooks{}

hooks.AddHook("test", func(ctx *gcli.HookCtx) bool {
buf.WriteString("fire the test hook")
return false
})

hooks.Fire("test", nil)
is.Eq("fire the test hook", buf.ResetGet())

hooks.Fire("not-exist", nil)
hooks.On("*", func(ctx *gcli.HookCtx) bool {
buf.WriteString("fire the * hook")
return false
})

// add prefix hook
hooks.On("app.test.*", func(ctx *gcli.HookCtx) bool {
buf.WriteString("fire the app.test.* hook")
return false
})

hooks.Fire("app.test.init", nil)

s := buf.ResetGet()
is.StrContains(s, "fire the app.test.* hook")
is.StrContains(s, "fire the * hook")
}
5 changes: 2 additions & 3 deletions gflag/opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/gookit/gcli/v3/helper"
"github.com/gookit/goutil/cflag"
"github.com/gookit/goutil/mathutil"
"github.com/gookit/goutil/stdutil"
"github.com/gookit/goutil/structs"
"github.com/gookit/goutil/strutil"
)
Expand Down Expand Up @@ -632,9 +631,9 @@ func (m *CliOpt) Flag() *flag.Flag {
}

// DValue wrap the default value
func (m *CliOpt) DValue() *stdutil.Value {
func (m *CliOpt) DValue() *structs.Value {
if m.defVal == nil {
m.defVal = &stdutil.Value{V: m.DefVal}
m.defVal = &structs.Value{V: m.DefVal}
}
return m.defVal
}
4 changes: 2 additions & 2 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

"github.com/gookit/color"
"github.com/gookit/gcli/v3/helper"
"github.com/gookit/goutil/stdutil"
"github.com/gookit/goutil/goinfo"
)

/*************************************************************
Expand Down Expand Up @@ -39,7 +39,7 @@ func logf(level VerbLevel, format string, v ...any) {
}

name := level2color[level].Render(level.Upper())
logAt := stdutil.GetCallerInfo(3)
logAt := goinfo.GetCallerInfo(3)

color.Printf("GCli: [%s] [<gray>%s</>] %s \n", name, logAt, fmt.Sprintf(format, v...))
}
Expand Down

0 comments on commit 4566d6a

Please sign in to comment.