mirror of
https://github.com/foomo/posh.git
synced 2025-10-16 12:45:38 +00:00
feat: extend command tree
This commit is contained in:
parent
81ae13c6db
commit
c87c709849
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
intenv "github.com/foomo/posh/internal/env"
|
intenv "github.com/foomo/posh/internal/env"
|
||||||
intlog "github.com/foomo/posh/internal/log"
|
intlog "github.com/foomo/posh/internal/log"
|
||||||
|
"github.com/foomo/posh/pkg/log"
|
||||||
"github.com/foomo/posh/pkg/plugin"
|
"github.com/foomo/posh/pkg/plugin"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -41,6 +42,7 @@ func Init(provider plugin.Provider) {
|
|||||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
func Execute() {
|
func Execute() {
|
||||||
code := 0
|
code := 0
|
||||||
|
l = log.NewFmt()
|
||||||
|
|
||||||
// handle interrupt
|
// handle interrupt
|
||||||
osInterrupt := make(chan os.Signal, 1)
|
osInterrupt := make(chan os.Signal, 1)
|
||||||
|
|||||||
@ -16,11 +16,6 @@ env:
|
|||||||
- name: GOPROXY
|
- name: GOPROXY
|
||||||
value: "https://proxy.golang.org,direct"
|
value: "https://proxy.golang.org,direct"
|
||||||
|
|
||||||
## Plugin settings
|
|
||||||
plugin:
|
|
||||||
source: .posh/plugin.go
|
|
||||||
provider: New
|
|
||||||
|
|
||||||
## Ownbrew settings
|
## Ownbrew settings
|
||||||
ownbrew:
|
ownbrew:
|
||||||
binDir: "bin"
|
binDir: "bin"
|
||||||
@ -60,7 +55,15 @@ require:
|
|||||||
|
|
||||||
## Required scripts that need to succeed
|
## Required scripts that need to succeed
|
||||||
scripts: []
|
scripts: []
|
||||||
## Example: require
|
## Example: require 1Password account
|
||||||
|
#- name: op
|
||||||
|
# command: |
|
||||||
|
# [[ $(op account --account <ACCOUNT> get 2>&1) =~ "found no account" ]] && exit 1 || exit 0
|
||||||
|
# help: |
|
||||||
|
# You're 1Password account is not registered yet! Please do so by running:
|
||||||
|
#
|
||||||
|
# $ op account add --address <ACCOUNT>.1password.eu --email <EMAIL>
|
||||||
|
## Example: npm
|
||||||
#- name: npm
|
#- name: npm
|
||||||
# command: npm whoami --registry=https://npm.pkg.github.com > /dev/null 2>&1
|
# command: npm whoami --registry=https://npm.pkg.github.com > /dev/null 2>&1
|
||||||
# help: |
|
# help: |
|
||||||
@ -138,13 +141,13 @@ require:
|
|||||||
# $ brew install teleport
|
# $ brew install teleport
|
||||||
|
|
||||||
## Example: goimports
|
## Example: goimports
|
||||||
# - name: goimports
|
#- name: goimports
|
||||||
# version: '>=2022'
|
# version: '>=2022'
|
||||||
# command: date -r $(which goimports) +%Y.%-m.%-d
|
# command: date -r $(which goimports) +%Y.%-m.%-d
|
||||||
# help: |
|
# help: |
|
||||||
# Please ensure you have 'goimports' installed in a recent version: %s!
|
# Please ensure you have 'goimports' installed in a recent version: %s!
|
||||||
#
|
#
|
||||||
# $ go install golang.org/x/tools/cmd/goimports@latest
|
# $ go install golang.org/x/tools/cmd/goimports@latest
|
||||||
|
|
||||||
|
|
||||||
## Integrations
|
## Integrations
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
*.so
|
|
||||||
*.lock
|
*.lock
|
||||||
/config/
|
/config/
|
||||||
/tmp/
|
/tmp/
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
module {{ .module }}/posh
|
module {{ .module }}/posh
|
||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
|
replace (
|
||||||
|
github.com/c-bata/go-prompt v0.2.6 => github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5
|
||||||
|
)
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
module {{.module}}
|
|
||||||
|
|
||||||
go 1.19
|
|
||||||
|
|
||||||
replace (
|
|
||||||
github.com/c-bata/go-prompt v0.2.6 => github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5
|
|
||||||
)
|
|
||||||
3
go.mod
3
go.mod
@ -16,6 +16,7 @@ require (
|
|||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/viper v1.14.0
|
github.com/spf13/viper v1.14.0
|
||||||
|
github.com/stretchr/testify v1.8.1
|
||||||
github.com/whilp/git-urls v1.0.0
|
github.com/whilp/git-urls v1.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ require (
|
|||||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||||
github.com/cloudflare/circl v1.1.0 // indirect
|
github.com/cloudflare/circl v1.1.0 // indirect
|
||||||
github.com/containerd/console v1.0.3 // indirect
|
github.com/containerd/console v1.0.3 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
@ -58,6 +60,7 @@ require (
|
|||||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.2.3 // indirect
|
github.com/pjbgf/sha1cd v0.2.3 // indirect
|
||||||
github.com/pkg/term v1.1.0 // indirect
|
github.com/pkg/term v1.1.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/sergi/go-diff v1.2.0 // indirect
|
github.com/sergi/go-diff v1.2.0 // indirect
|
||||||
github.com/shopspring/decimal v1.2.0 // indirect
|
github.com/shopspring/decimal v1.2.0 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@ -314,6 +314,7 @@ github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
|
|||||||
github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
|
github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
@ -322,6 +323,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||||
github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU=
|
github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU=
|
||||||
|
|||||||
@ -28,9 +28,6 @@ type (
|
|||||||
ArgumentCompleter interface {
|
ArgumentCompleter interface {
|
||||||
CompleteArguments(ctx context.Context, r *readline.Readline, d prompt.Document) []prompt.Suggest
|
CompleteArguments(ctx context.Context, r *readline.Readline, d prompt.Document) []prompt.Suggest
|
||||||
}
|
}
|
||||||
PassThroughArgsCompleter interface {
|
|
||||||
CompletePassTroughArgs(ctx context.Context, r *readline.Readline, d prompt.Document) []prompt.Suggest
|
|
||||||
}
|
|
||||||
PassThroughFlagsCompleter interface {
|
PassThroughFlagsCompleter interface {
|
||||||
CompletePassTroughFlags(ctx context.Context, r *readline.Readline, d prompt.Document) []prompt.Suggest
|
CompletePassTroughFlags(ctx context.Context, r *readline.Readline, d prompt.Document) []prompt.Suggest
|
||||||
}
|
}
|
||||||
|
|||||||
12
pkg/command/tree/errors.go
Normal file
12
pkg/command/tree/errors.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package tree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoop = errors.New("noop")
|
||||||
|
ErrInvalidCommand = errors.New("invalid command")
|
||||||
|
ErrMissingCommand = errors.New("missing command")
|
||||||
|
ErrMissingArgument = errors.New("missing argument")
|
||||||
|
)
|
||||||
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
Name string
|
Name string
|
||||||
Names func() []string
|
Values func(ctx context.Context, r *readline.Readline) []string
|
||||||
Args Args
|
Args Args
|
||||||
Flags func(fs *readline.FlagSet)
|
Flags func(fs *readline.FlagSet)
|
||||||
PassThroughArgs Args
|
PassThroughArgs Args
|
||||||
@ -51,7 +51,13 @@ func (c *Node) completeArguments(ctx context.Context, p *Root, r *readline.Readl
|
|||||||
switch {
|
switch {
|
||||||
case len(c.Nodes) > 0 && len(localArgs) <= 1:
|
case len(c.Nodes) > 0 && len(localArgs) <= 1:
|
||||||
for _, command := range c.Nodes {
|
for _, command := range c.Nodes {
|
||||||
suggest = append(suggest, prompt.Suggest{Text: command.Name, Description: command.Description})
|
if command.Values != nil {
|
||||||
|
for _, name := range command.Values(ctx, r) {
|
||||||
|
suggest = append(suggest, prompt.Suggest{Text: name, Description: command.Description})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
suggest = append(suggest, prompt.Suggest{Text: command.Name, Description: command.Description})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case len(c.Args) > 0 && len(c.Args) >= len(localArgs):
|
case len(c.Args) > 0 && len(c.Args) >= len(localArgs):
|
||||||
j := len(localArgs)
|
j := len(localArgs)
|
||||||
@ -89,11 +95,11 @@ func (c *Node) execute(ctx context.Context, r *readline.Readline, i int) error {
|
|||||||
localArgs := r.Args()[i:]
|
localArgs := r.Args()[i:]
|
||||||
switch {
|
switch {
|
||||||
case len(c.Nodes) > 0 && len(localArgs) == 0:
|
case len(c.Nodes) > 0 && len(localArgs) == 0:
|
||||||
return errors.New("missing [command] argument")
|
return ErrMissingCommand
|
||||||
case len(c.Args) > 0:
|
case len(c.Args) > 0:
|
||||||
for j, arg := range c.Args {
|
for j, arg := range c.Args {
|
||||||
if !arg.Optional && len(localArgs)-2 < j {
|
if !arg.Optional && len(localArgs)-1 < j {
|
||||||
return errors.New("missing [" + arg.Name + "] argument")
|
return errors.Wrap(ErrMissingArgument, arg.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
"github.com/c-bata/go-prompt"
|
"github.com/c-bata/go-prompt"
|
||||||
"github.com/foomo/posh/pkg/readline"
|
"github.com/foomo/posh/pkg/readline"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Root struct {
|
type Root struct {
|
||||||
@ -25,17 +24,23 @@ func (t *Root) RunExecution(ctx context.Context, r *readline.Readline) error {
|
|||||||
cmd *Node
|
cmd *Node
|
||||||
index int
|
index int
|
||||||
)
|
)
|
||||||
if r.Args().LenIs(0) {
|
|
||||||
cmd = t.Node
|
switch {
|
||||||
} else if found, i := t.find(t.Nodes, r, 0); found != nil {
|
case t.Node == nil && len(t.Nodes) == 0:
|
||||||
cmd = found
|
return ErrNoop
|
||||||
index = i
|
case r.Args().LenIs(0) && t.Node == nil:
|
||||||
} else {
|
return ErrMissingCommand
|
||||||
cmd = t.Node
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd == nil {
|
if r.Args().LenIs(0) {
|
||||||
return errors.New("invalid command")
|
cmd = t.Node
|
||||||
|
} else if found, i := t.find(ctx, t.Nodes, r, 0); found != nil {
|
||||||
|
cmd = found
|
||||||
|
index = i
|
||||||
|
} else if t.Node == nil {
|
||||||
|
return ErrInvalidCommand
|
||||||
|
} else {
|
||||||
|
cmd = t.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cmd.setFlags(r, true); err != nil {
|
if err := cmd.setFlags(r, true); err != nil {
|
||||||
@ -52,9 +57,15 @@ func (t *Root) RunCompletion(ctx context.Context, r *readline.Readline) []prompt
|
|||||||
case readline.ModeArgs:
|
case readline.ModeArgs:
|
||||||
if r.Args().LenLte(1) && len(t.Nodes) > 0 {
|
if r.Args().LenLte(1) && len(t.Nodes) > 0 {
|
||||||
for _, command := range t.Nodes {
|
for _, command := range t.Nodes {
|
||||||
suggests = append(suggests, prompt.Suggest{Text: command.Name, Description: command.Description})
|
if command.Values != nil {
|
||||||
|
for _, name := range command.Values(ctx, r) {
|
||||||
|
suggests = append(suggests, prompt.Suggest{Text: name, Description: command.Description})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
suggests = append(suggests, prompt.Suggest{Text: command.Name, Description: command.Description})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if cmd, i := t.find(t.Nodes, r, 0); cmd == nil && t.Node != nil {
|
} else if cmd, i := t.find(ctx, t.Nodes, r, 0); cmd == nil && t.Node != nil {
|
||||||
if err := t.Node.setFlags(r, false); err != nil {
|
if err := t.Node.setFlags(r, false); err != nil {
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
@ -68,7 +79,7 @@ func (t *Root) RunCompletion(ctx context.Context, r *readline.Readline) []prompt
|
|||||||
suggests = cmd.completeArguments(ctx, t, r, i+1)
|
suggests = cmd.completeArguments(ctx, t, r, i+1)
|
||||||
}
|
}
|
||||||
case readline.ModeFlags:
|
case readline.ModeFlags:
|
||||||
if cmd, _ := t.find(t.Nodes, r, 0); cmd == nil && t.Node != nil {
|
if cmd, _ := t.find(ctx, t.Nodes, r, 0); cmd == nil && t.Node != nil {
|
||||||
if err := t.Node.setFlags(r, false); err != nil {
|
if err := t.Node.setFlags(r, false); err != nil {
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
@ -81,10 +92,8 @@ func (t *Root) RunCompletion(ctx context.Context, r *readline.Readline) []prompt
|
|||||||
} else {
|
} else {
|
||||||
suggests = cmd.completeFlags(r)
|
suggests = cmd.completeFlags(r)
|
||||||
}
|
}
|
||||||
case readline.ModePassThroughArgs:
|
|
||||||
// TODO
|
|
||||||
case readline.ModePassThroughFlags:
|
case readline.ModePassThroughFlags:
|
||||||
if cmd, _ := t.find(t.Nodes, r, 0); cmd == nil && t.Node != nil {
|
if cmd, _ := t.find(ctx, t.Nodes, r, 0); cmd == nil && t.Node != nil {
|
||||||
if err := t.Node.setFlags(r, false); err != nil {
|
if err := t.Node.setFlags(r, false); err != nil {
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
@ -110,26 +119,26 @@ func (t *Root) RunCompletion(ctx context.Context, r *readline.Readline) []prompt
|
|||||||
// ~ Private methods
|
// ~ Private methods
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
func (t *Root) find(cmds []*Node, r *readline.Readline, i int) (*Node, int) {
|
func (t *Root) find(ctx context.Context, cmds []*Node, r *readline.Readline, i int) (*Node, int) {
|
||||||
if r.Args().LenLt(i + 1) {
|
if r.Args().LenLt(i + 1) {
|
||||||
return nil, i
|
return nil, i
|
||||||
}
|
}
|
||||||
arg := r.Args().At(i)
|
arg := r.Args().At(i)
|
||||||
for _, cmd := range cmds {
|
for _, cmd := range cmds {
|
||||||
if cmd.Name == arg {
|
if cmd.Name == arg {
|
||||||
if subCmd, j := t.find(cmd.Nodes, r, i+1); subCmd != nil {
|
if subCmd, j := t.find(ctx, cmd.Nodes, r, i+1); subCmd != nil {
|
||||||
return subCmd, j
|
return subCmd, j
|
||||||
}
|
}
|
||||||
return cmd, i
|
return cmd, i
|
||||||
}
|
}
|
||||||
if cmd.Names != nil {
|
if cmd.Values != nil {
|
||||||
for _, name := range cmd.Names() {
|
for _, name := range cmd.Values(ctx, r) {
|
||||||
if name == arg {
|
if name == arg {
|
||||||
if subCmd, j := t.find(cmd.Nodes, r, i+1); subCmd != nil {
|
if subCmd, j := t.find(ctx, cmd.Nodes, r, i+1); subCmd != nil {
|
||||||
return subCmd, j
|
return subCmd, j
|
||||||
}
|
}
|
||||||
|
return cmd, i
|
||||||
}
|
}
|
||||||
return cmd, i
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
427
pkg/command/tree/root_test.go
Normal file
427
pkg/command/tree/root_test.go
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
package tree_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/foomo/posh/pkg/command/tree"
|
||||||
|
"github.com/foomo/posh/pkg/log"
|
||||||
|
"github.com/foomo/posh/pkg/readline"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrOK = errors.New("ok")
|
||||||
|
|
||||||
|
func TestRoot(t *testing.T) {
|
||||||
|
l := log.NewTest(t, log.TestWithLevel(log.LevelInfo))
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrRoot = errors.New("root")
|
||||||
|
ErrFirst = errors.New("first")
|
||||||
|
ErrSecond = errors.New("second")
|
||||||
|
ErrSecond1 = errors.New("second-1")
|
||||||
|
ErrSecond2 = errors.New("second-2")
|
||||||
|
ErrThird = errors.New("third")
|
||||||
|
ErrThird1 = errors.New("third1")
|
||||||
|
)
|
||||||
|
|
||||||
|
r := &tree.Root{
|
||||||
|
Name: "root",
|
||||||
|
Description: "root tree",
|
||||||
|
Node: &tree.Node{
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
return ErrRoot
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Nodes: tree.Nodes{
|
||||||
|
{
|
||||||
|
Name: "first",
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
return ErrFirst
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "second",
|
||||||
|
Nodes: tree.Nodes{
|
||||||
|
{
|
||||||
|
Name: "second-1",
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
return ErrSecond1
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "second-2",
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
return ErrSecond2
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
return ErrSecond
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "third",
|
||||||
|
Values: func(ctx context.Context, r *readline.Readline) []string {
|
||||||
|
return []string{"third-a", "third-b"}
|
||||||
|
},
|
||||||
|
Nodes: tree.Nodes{
|
||||||
|
{
|
||||||
|
Name: "third-1",
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
return ErrThird1
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
return ErrThird
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
wantErr assert.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "tree",
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrRoot)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree first",
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrFirst)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree first foo",
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrFirst)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree second",
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrSecond)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree second second-1",
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrSecond1)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree second second-2",
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrSecond2)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree second second-3",
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrSecond)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree third-a",
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrThird)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree third-b",
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrThird)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree third-c",
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrRoot)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree third-a third-1",
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrThird1)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rl, err := readline.New(l)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t1 *testing.T) {
|
||||||
|
require.NoError(t1, rl.Parse(tt.name))
|
||||||
|
if !tt.wantErr(t1, r.RunExecution(context.WithValue(ctx, "t", t1), rl)) {
|
||||||
|
l.Warn(rl.String())
|
||||||
|
} else {
|
||||||
|
l.Debug(rl.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoot_Node(t *testing.T) {
|
||||||
|
l := log.NewTest(t, log.TestWithLevel(log.LevelInfo))
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
root *tree.Root
|
||||||
|
wantErr assert.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "tree",
|
||||||
|
root: &tree.Root{},
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, tree.ErrNoop)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree",
|
||||||
|
root: &tree.Root{
|
||||||
|
Node: &tree.Node{
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
return ErrOK
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrOK)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree one",
|
||||||
|
root: &tree.Root{
|
||||||
|
Node: &tree.Node{
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
assert.Equal(T(ctx), "one", r.Args().At(0))
|
||||||
|
return ErrOK
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrOK)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rl, err := readline.New(l)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t1 *testing.T) {
|
||||||
|
require.NoError(t1, rl.Parse(tt.name))
|
||||||
|
if !tt.wantErr(t1, tt.root.RunExecution(context.WithValue(ctx, "t", t1), rl)) {
|
||||||
|
l.Warn(rl.String())
|
||||||
|
} else {
|
||||||
|
l.Debug(rl.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoot_NodeArgs(t *testing.T) {
|
||||||
|
l := log.NewTest(t, log.TestWithLevel(log.LevelInfo))
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
root *tree.Root
|
||||||
|
wantErr assert.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "tree",
|
||||||
|
root: &tree.Root{
|
||||||
|
Node: &tree.Node{
|
||||||
|
Args: tree.Args{
|
||||||
|
{
|
||||||
|
Name: "first",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
return ErrOK
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, tree.ErrMissingArgument)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree one",
|
||||||
|
root: &tree.Root{
|
||||||
|
Node: &tree.Node{
|
||||||
|
Args: tree.Args{
|
||||||
|
{
|
||||||
|
Name: "first",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
assert.Equal(T(ctx), "one", r.Args().At(0))
|
||||||
|
return ErrOK
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrOK)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree",
|
||||||
|
root: &tree.Root{
|
||||||
|
Node: &tree.Node{
|
||||||
|
Args: tree.Args{
|
||||||
|
{
|
||||||
|
Name: "first",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "second",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
return ErrOK
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, tree.ErrMissingArgument)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree one",
|
||||||
|
root: &tree.Root{
|
||||||
|
Node: &tree.Node{
|
||||||
|
Args: tree.Args{
|
||||||
|
{
|
||||||
|
Name: "first",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "second",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
return ErrOK
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, tree.ErrMissingArgument)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree one two",
|
||||||
|
root: &tree.Root{
|
||||||
|
Node: &tree.Node{
|
||||||
|
Args: tree.Args{
|
||||||
|
{
|
||||||
|
Name: "first",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "second",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
assert.Equal(T(ctx), "one", r.Args().At(0))
|
||||||
|
assert.Equal(T(ctx), "two", r.Args().At(1))
|
||||||
|
return ErrOK
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrOK)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rl, err := readline.New(l)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t1 *testing.T) {
|
||||||
|
require.NoError(t1, rl.Parse(tt.name))
|
||||||
|
if !tt.wantErr(t1, tt.root.RunExecution(context.WithValue(ctx, "t", t1), rl)) {
|
||||||
|
l.Warn(rl.String())
|
||||||
|
} else {
|
||||||
|
l.Debug(rl.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoot_NodeFlags(t *testing.T) {
|
||||||
|
l := log.NewTest(t, log.TestWithLevel(log.LevelDebug))
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
root *tree.Root
|
||||||
|
wantErr assert.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "tree",
|
||||||
|
root: &tree.Root{
|
||||||
|
Node: &tree.Node{
|
||||||
|
Flags: func(fs *readline.FlagSet) {
|
||||||
|
fs.String("first", "first", "first")
|
||||||
|
fs.Bool("second", false, "second")
|
||||||
|
fs.Int64("third", 0, "third")
|
||||||
|
},
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
assert.Equal(T(ctx), "first", r.FlagSet().GetString("first"))
|
||||||
|
assert.False(T(ctx), r.FlagSet().GetBool("second"))
|
||||||
|
assert.Equal(T(ctx), int64(0), r.FlagSet().GetInt64("third"))
|
||||||
|
return ErrOK
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrOK)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree --first one --second --third 13",
|
||||||
|
root: &tree.Root{
|
||||||
|
Node: &tree.Node{
|
||||||
|
Flags: func(fs *readline.FlagSet) {
|
||||||
|
fs.String("first", "first", "first")
|
||||||
|
fs.Bool("second", false, "second")
|
||||||
|
fs.Int64("third", 0, "third")
|
||||||
|
},
|
||||||
|
Execute: func(ctx context.Context, r *readline.Readline) error {
|
||||||
|
assert.Equal(T(ctx), "one", r.FlagSet().GetString("first"))
|
||||||
|
assert.True(T(ctx), r.FlagSet().GetBool("second"))
|
||||||
|
assert.Equal(T(ctx), int64(13), r.FlagSet().GetInt64("third"))
|
||||||
|
return ErrOK
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
||||||
|
return assert.ErrorIs(t, err, ErrOK)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rl, err := readline.New(l)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t1 *testing.T) {
|
||||||
|
require.NoError(t1, rl.Parse(tt.name))
|
||||||
|
if !tt.wantErr(t1, tt.root.RunExecution(context.WithValue(ctx, "t", t1), rl)) {
|
||||||
|
l.Warn(rl.String())
|
||||||
|
} else {
|
||||||
|
l.Debug(rl.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func T(ctx context.Context) *testing.T {
|
||||||
|
return ctx.Value("t").(*testing.T)
|
||||||
|
}
|
||||||
@ -1,15 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Plugin struct {
|
|
||||||
Source string `json:"source" yaml:"source"`
|
|
||||||
Target string `json:"target" yaml:"target"`
|
|
||||||
Provider string `json:"provider" yaml:"provider"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Plugin) String() string {
|
|
||||||
return fmt.Sprintf("Filename: %s, Provider: %s", c.Source, c.Provider)
|
|
||||||
}
|
|
||||||
155
pkg/log/test.go
Normal file
155
pkg/log/test.go
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Test struct {
|
||||||
|
t *testing.T
|
||||||
|
name string
|
||||||
|
level Level
|
||||||
|
}
|
||||||
|
TestOption func(*Test)
|
||||||
|
)
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// ~ Options
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func TestWithLevel(v Level) TestOption {
|
||||||
|
return func(o *Test) {
|
||||||
|
o.level = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// ~ Constructor
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func NewTest(t *testing.T, opts ...TestOption) *Test {
|
||||||
|
inst := &Test{
|
||||||
|
t: t,
|
||||||
|
level: LevelError,
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
if opt != nil {
|
||||||
|
opt(inst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// ~ Public methods
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func (l *Test) Level() Level {
|
||||||
|
return l.level
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) IsLevel(v Level) bool {
|
||||||
|
return l.level <= v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Named(name string) Logger {
|
||||||
|
clone := *l
|
||||||
|
clone.name = name
|
||||||
|
return &clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Print(a ...interface{}) {
|
||||||
|
l.t.Log(l.prefix("", a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Printf(format string, a ...interface{}) {
|
||||||
|
l.Print(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Success(a ...interface{}) {
|
||||||
|
l.t.Log(l.prefix("success", a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Successf(format string, a ...interface{}) {
|
||||||
|
l.Success(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Trace(a ...interface{}) {
|
||||||
|
if l.IsLevel(LevelTrace) {
|
||||||
|
l.t.Log(l.prefix("trace", a)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Tracef(format string, a ...interface{}) {
|
||||||
|
l.Trace(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Debug(a ...interface{}) {
|
||||||
|
if l.IsLevel(LevelDebug) {
|
||||||
|
l.t.Log(l.prefix("debug", a)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Debugf(format string, a ...interface{}) {
|
||||||
|
l.Debug(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Info(a ...interface{}) {
|
||||||
|
if l.IsLevel(LevelInfo) {
|
||||||
|
l.t.Log(l.prefix("info", a)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Infof(format string, a ...interface{}) {
|
||||||
|
l.Info(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Warn(a ...interface{}) {
|
||||||
|
if l.IsLevel(LevelWarn) {
|
||||||
|
l.t.Log(l.prefix("warn", a)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Warnf(format string, a ...interface{}) {
|
||||||
|
l.Warn(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Error(a ...interface{}) {
|
||||||
|
if l.IsLevel(LevelError) {
|
||||||
|
l.t.Error(l.prefix("error", a)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Errorf(format string, a ...interface{}) {
|
||||||
|
l.Error(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Fatal(a ...interface{}) {
|
||||||
|
l.t.Fatal(l.prefix("fatal", a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Fatalf(format string, a ...interface{}) {
|
||||||
|
l.Fatal(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Test) Must(err error) {
|
||||||
|
if err != nil {
|
||||||
|
l.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// ~ Private methods
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func (l *Test) prefix(level string, a []any) []any {
|
||||||
|
var ret []interface{}
|
||||||
|
if level != "" {
|
||||||
|
ret = append(ret, level+":")
|
||||||
|
}
|
||||||
|
if l.name != "" && l.IsLevel(LevelDebug) {
|
||||||
|
ret = append(ret, fmt.Sprintf("[%s]", l.name))
|
||||||
|
}
|
||||||
|
return append(ret, a...)
|
||||||
|
}
|
||||||
@ -318,12 +318,6 @@ func (s *Prompt) complete(d prompt.Document) []prompt.Suggest {
|
|||||||
} else if value, ok := cmd.(command.Completer); ok {
|
} else if value, ok := cmd.(command.Completer); ok {
|
||||||
return s.filter(value.Complete(ctx, s.readline, d), word, true)
|
return s.filter(value.Complete(ctx, s.readline, d), word, true)
|
||||||
}
|
}
|
||||||
case readline.ModePassThroughArgs:
|
|
||||||
if value, ok := cmd.(command.PassThroughArgsCompleter); ok {
|
|
||||||
return s.filter(value.CompletePassTroughArgs(ctx, s.readline, d), word, true)
|
|
||||||
} else if value, ok := cmd.(command.Completer); ok {
|
|
||||||
return s.filter(value.Complete(ctx, s.readline, d), word, true)
|
|
||||||
}
|
|
||||||
case readline.ModePassThroughFlags:
|
case readline.ModePassThroughFlags:
|
||||||
if value, ok := cmd.(command.PassThroughFlagsCompleter); ok {
|
if value, ok := cmd.(command.PassThroughFlagsCompleter); ok {
|
||||||
return s.filter(value.CompletePassTroughFlags(ctx, s.readline, d), word, true)
|
return s.filter(value.CompletePassTroughFlags(ctx, s.readline, d), word, true)
|
||||||
|
|||||||
@ -70,3 +70,20 @@ func (a Args) Last() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a Args) IndexOf(v string) int {
|
||||||
|
for i, s := range a {
|
||||||
|
if s == v {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Args) Slice(start, end int) Args {
|
||||||
|
return append(a[:start], a[end:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Args) Splice(start, num int) Args {
|
||||||
|
return append(a[:start], a[start+num:]...)
|
||||||
|
}
|
||||||
|
|||||||
@ -5,7 +5,6 @@ type Mode string
|
|||||||
const (
|
const (
|
||||||
ModeArgs Mode = ""
|
ModeArgs Mode = ""
|
||||||
ModeFlags Mode = "flags"
|
ModeFlags Mode = "flags"
|
||||||
ModePassThroughArgs Mode = "passThrough"
|
|
||||||
ModePassThroughFlags Mode = "passThroughFlags"
|
ModePassThroughFlags Mode = "passThroughFlags"
|
||||||
ModeAdditionalArgs Mode = "additional"
|
ModeAdditionalArgs Mode = "additional"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -18,7 +18,6 @@ type (
|
|||||||
args Args
|
args Args
|
||||||
flags Args
|
flags Args
|
||||||
flagSet *FlagSet
|
flagSet *FlagSet
|
||||||
passThroughArgs Args
|
|
||||||
passThroughFlags Args
|
passThroughFlags Args
|
||||||
passThroughFlagSet *FlagSet
|
passThroughFlagSet *FlagSet
|
||||||
additionalArgs Args
|
additionalArgs Args
|
||||||
@ -92,12 +91,6 @@ func (a *Readline) FlagSet() *FlagSet {
|
|||||||
return a.flagSet
|
return a.flagSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Readline) PassThroughArgs() Args {
|
|
||||||
a.mu.RLock()
|
|
||||||
defer a.mu.RUnlock()
|
|
||||||
return a.passThroughArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Readline) PassThroughFlags() Args {
|
func (a *Readline) PassThroughFlags() Args {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
@ -138,9 +131,6 @@ func (a *Readline) Parse(input string) error {
|
|||||||
a.mode = ModeFlags
|
a.mode = ModeFlags
|
||||||
}
|
}
|
||||||
if i != last && (a.mode == ModeArgs || a.mode == ModeFlags) && Arg(part).IsPass() {
|
if i != last && (a.mode == ModeArgs || a.mode == ModeFlags) && Arg(part).IsPass() {
|
||||||
a.mode = ModePassThroughArgs
|
|
||||||
}
|
|
||||||
if a.mode == ModePassThroughArgs && Arg(part).IsFlag() {
|
|
||||||
a.mode = ModePassThroughFlags
|
a.mode = ModePassThroughFlags
|
||||||
}
|
}
|
||||||
if Arg(part).IsAdditional() && i < len(parts)-1 {
|
if Arg(part).IsAdditional() && i < len(parts)-1 {
|
||||||
@ -152,8 +142,6 @@ func (a *Readline) Parse(input string) error {
|
|||||||
a.args = append(a.args, part)
|
a.args = append(a.args, part)
|
||||||
case ModeFlags:
|
case ModeFlags:
|
||||||
a.flags = append(a.flags, part)
|
a.flags = append(a.flags, part)
|
||||||
case ModePassThroughArgs:
|
|
||||||
a.passThroughArgs = append(a.passThroughArgs, part)
|
|
||||||
case ModePassThroughFlags:
|
case ModePassThroughFlags:
|
||||||
a.passThroughFlags = append(a.passThroughFlags, part)
|
a.passThroughFlags = append(a.passThroughFlags, part)
|
||||||
case ModeAdditionalArgs:
|
case ModeAdditionalArgs:
|
||||||
@ -200,10 +188,9 @@ Cmd: %s
|
|||||||
Mode %s
|
Mode %s
|
||||||
Args: %s
|
Args: %s
|
||||||
Flags: %s
|
Flags: %s
|
||||||
PassThroughArgs: %s
|
|
||||||
PassThroughFlags: %s
|
PassThroughFlags: %s
|
||||||
AdditionalArgs %s
|
AdditionalArgs %s
|
||||||
`, a.Cmd(), a.Mode(), a.Args(), a.Flags(), a.PassThroughArgs(), a.PassThroughFlags(), a.AdditionalArgs())
|
`, a.Cmd(), a.Mode(), a.Args(), a.Flags(), a.PassThroughFlags(), a.AdditionalArgs())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Readline) IsModeDefault() bool {
|
func (a *Readline) IsModeDefault() bool {
|
||||||
@ -211,7 +198,7 @@ func (a *Readline) IsModeDefault() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Readline) IsModePassThrough() bool {
|
func (a *Readline) IsModePassThrough() bool {
|
||||||
return a.Mode() == ModePassThroughArgs
|
return a.Mode() == ModePassThroughFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Readline) IsModeAdditional() bool {
|
func (a *Readline) IsModeAdditional() bool {
|
||||||
@ -258,7 +245,6 @@ func (a *Readline) reset() {
|
|||||||
a.args = nil
|
a.args = nil
|
||||||
a.flags = nil
|
a.flags = nil
|
||||||
a.flagSet = nil
|
a.flagSet = nil
|
||||||
a.passThroughArgs = nil
|
|
||||||
a.passThroughFlags = nil
|
a.passThroughFlags = nil
|
||||||
a.passThroughFlagSet = nil
|
a.passThroughFlagSet = nil
|
||||||
a.additionalArgs = nil
|
a.additionalArgs = nil
|
||||||
|
|||||||
22
pkg/util/files/mkdirall.go
Normal file
22
pkg/util/files/mkdirall.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package files
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MkdirAll(paths ...string) error {
|
||||||
|
for _, path := range paths {
|
||||||
|
if stat, err := os.Stat(path); err != nil && os.IsNotExist(err) {
|
||||||
|
if err := os.MkdirAll(path, os.ModeDir|0722); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
} else if !stat.IsDir() {
|
||||||
|
return errors.Errorf("%s not a directory", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user