Compare commits
6 Commits
65a754363f
...
v0.1.2
| Author | SHA1 | Date | |
|---|---|---|---|
| ad5cf18535 | |||
|
|
1151e7c3ad | ||
| 44d8837dbc | |||
|
|
0b6a6ee99b | ||
| 7771ff495d | |||
|
|
662cbdb510 |
2
app.go
2
app.go
@@ -111,7 +111,7 @@ func (a *App) list(ctx context.Context) error {
|
||||
}
|
||||
|
||||
arr := &input.Array{}
|
||||
arr.SetArgument("command_name", value.New(CommandList))
|
||||
arr.SetArgument(ArgumentCommandName, value.New(CommandList))
|
||||
in := input.Chain(arr, a.in)
|
||||
|
||||
return Run(ctx, cmd, in, a.out)
|
||||
|
||||
@@ -100,6 +100,10 @@ func (c *Command) With(opts ...Option) *Command {
|
||||
|
||||
// Run run command with input and output.
|
||||
func (c *Command) Run(ctx context.Context, in input.Input, out output.Output) error {
|
||||
if c.Execute == nil {
|
||||
return fmt.Errorf("%w", ErrExecuteNil)
|
||||
}
|
||||
|
||||
if c.Handle != nil {
|
||||
return c.Handle(ctx, in, out, c.Execute)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package console_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
@@ -43,6 +44,7 @@ func Command() *console.Command {
|
||||
option.String("string", "array string", option.Array),
|
||||
option.Bool("bool", "test bool option"),
|
||||
option.Duration("duration", "test duration with default", option.Default(time.Second)),
|
||||
option.Time("hidden", "hidden time", option.Default(time.Second), option.Hidden),
|
||||
)
|
||||
|
||||
return nil
|
||||
@@ -124,3 +126,21 @@ func TestChainHandle(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunEmptyExecute(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
empty := console.Command{
|
||||
Name: "empty",
|
||||
}
|
||||
in := &input.Array{
|
||||
Map: input.Map{},
|
||||
}
|
||||
out := output.Stdout()
|
||||
|
||||
err := empty.Run(ctx, in, out)
|
||||
if !errors.Is(err, console.ErrExecuteNil) {
|
||||
t.Fatalf("expected: %v, got: %v ", console.ErrExecuteNil, err)
|
||||
}
|
||||
}
|
||||
|
||||
37
console.go
37
console.go
@@ -18,6 +18,15 @@ const (
|
||||
verboseInfo = 1
|
||||
)
|
||||
|
||||
const (
|
||||
OptionHelp = "help"
|
||||
OptionVersion = "version"
|
||||
OptionAnsi = "ansi"
|
||||
OptionNoAnsi = "no-ansi"
|
||||
OptionQuiet = "quiet"
|
||||
OptionVerbose = "verbose"
|
||||
)
|
||||
|
||||
// Execute the current command with option.
|
||||
func Execute(ctx context.Context, cmd *Command, opts ...func(*App)) {
|
||||
opts = append([]func(*App){WithSkipArgs(1)}, opts...)
|
||||
@@ -42,7 +51,7 @@ func Run(ctx context.Context, cmd *Command, in input.Input, out output.Output) e
|
||||
|
||||
out = verbose(ctx, in, out)
|
||||
|
||||
if in.Option(ctx, "version").Bool() {
|
||||
if in.Option(ctx, OptionVersion).Bool() {
|
||||
version := cmd.Version
|
||||
if version == "" {
|
||||
version = "unknown"
|
||||
@@ -53,7 +62,7 @@ func Run(ctx context.Context, cmd *Command, in input.Input, out output.Output) e
|
||||
return nil
|
||||
}
|
||||
|
||||
if in.Option(ctx, "help").Bool() {
|
||||
if in.Option(ctx, OptionHelp).Bool() {
|
||||
return showHelp(ctx, cmd, in, out)
|
||||
}
|
||||
|
||||
@@ -62,9 +71,9 @@ func Run(ctx context.Context, cmd *Command, in input.Input, out output.Output) e
|
||||
|
||||
func ansi(ctx context.Context, in input.Input, out output.Output) output.Output {
|
||||
switch {
|
||||
case in.Option(ctx, "ansi").Bool():
|
||||
case in.Option(ctx, OptionAnsi).Bool():
|
||||
out = output.Ansi(out)
|
||||
case in.Option(ctx, "no-ansi").Bool():
|
||||
case in.Option(ctx, OptionNoAnsi).Bool():
|
||||
out = output.None(out)
|
||||
case lookupEnv("NO_COLOR"):
|
||||
out = output.None(out)
|
||||
@@ -83,10 +92,10 @@ func lookupEnv(name string) bool {
|
||||
|
||||
func verbose(ctx context.Context, in input.Input, out output.Output) output.Output {
|
||||
switch {
|
||||
case in.Option(ctx, "quiet").Bool():
|
||||
case in.Option(ctx, OptionQuiet).Bool():
|
||||
out = output.Quiet()
|
||||
default:
|
||||
verb := in.Option(ctx, "verbose").Bools()
|
||||
verb := in.Option(ctx, OptionVerbose).Bools()
|
||||
|
||||
switch {
|
||||
case len(verb) == verboseInfo:
|
||||
@@ -105,8 +114,8 @@ func verbose(ctx context.Context, in input.Input, out output.Output) output.Outp
|
||||
|
||||
func showHelp(ctx context.Context, cmd *Command, in input.Input, out output.Output) error {
|
||||
arr := &input.Array{}
|
||||
arr.SetArgument(HelpArgumentCommandName, value.New(cmd.Name))
|
||||
arr.SetOption("help", value.New(false))
|
||||
arr.SetArgument(ArgumentCommandName, value.New(cmd.Name))
|
||||
arr.SetOption(OptionHelp, value.New(false))
|
||||
|
||||
if _, err := Find(cmd.Name); errors.Is(err, ErrNotFound) {
|
||||
register(cmd)
|
||||
@@ -125,13 +134,13 @@ func showHelp(ctx context.Context, cmd *Command, in input.Input, out output.Outp
|
||||
// Default options and argument command.
|
||||
func Default(d *input.Definition) *input.Definition {
|
||||
return d.SetOptions(
|
||||
option.Bool("no-ansi", "Disable ANSI output"),
|
||||
option.Bool("ansi", "Do not ask any interactive question"),
|
||||
option.Bool("version", "Display this application version", option.Short('V')),
|
||||
option.Bool("help", "Display this help message", option.Short('h')),
|
||||
option.Bool("verbose",
|
||||
option.Bool(OptionNoAnsi, "Disable ANSI output"),
|
||||
option.Bool(OptionAnsi, "Do not ask any interactive question"),
|
||||
option.Bool(OptionVersion, "Display this application version", option.Short('V')),
|
||||
option.Bool(OptionHelp, "Display this help message", option.Short('h')),
|
||||
option.Bool(OptionVerbose,
|
||||
"Increase the verbosity of messages: -v for info output, -vv for debug and -vvv for trace",
|
||||
option.Short('v'), option.Array),
|
||||
option.Bool("quiet", "Do not output any message", option.Short('q')),
|
||||
option.Bool(OptionQuiet, "Do not output any message", option.Short('q')),
|
||||
)
|
||||
}
|
||||
|
||||
31
error.go
Normal file
31
error.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("command not found")
|
||||
ErrCommandNil = errors.New("console: Register command is nil")
|
||||
ErrExecuteNil = errors.New("console: execute is nil")
|
||||
ErrCommandDuplicate = errors.New("console: duplicate command")
|
||||
)
|
||||
|
||||
type AlternativesError struct {
|
||||
Alt []string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e AlternativesError) Error() string {
|
||||
return fmt.Sprintf("%s, alternatives: [%s]", e.Err, strings.Join(e.Alt, ","))
|
||||
}
|
||||
|
||||
func (e AlternativesError) Is(err error) bool {
|
||||
return errors.Is(e.Err, err)
|
||||
}
|
||||
|
||||
func (e AlternativesError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
@@ -16,7 +16,8 @@ const (
|
||||
AppName = "console"
|
||||
)
|
||||
|
||||
// FDEVS_CONSOLE_CAT=env go run cmd/config/main.go fdevs:console:arg -b tmp.
|
||||
// FDEVS_CONSOLE_CAT=env FDEVS_CONSOLE_HIDDEN=2022-09-18T23:07:49+03:00 go run cmd/config/main.go fdevs:console:arg -b tmp.
|
||||
// FDEVS_CONSOLE_CAT=env go run cmd/config/main.go fdevs:console:arg --hidden=2022-09-18T23:07:49+03:00 -b tmp.
|
||||
func main() {
|
||||
env := config.New(Namespace, AppName, []config.Provider{
|
||||
env.New(),
|
||||
|
||||
@@ -20,6 +20,7 @@ func Args() *console.Command {
|
||||
option.String("bar", "required bar option", option.Required, option.Short('b')),
|
||||
option.String("cat", "cat option", option.Short('c')),
|
||||
option.Time("time", "time example"),
|
||||
option.Time("hidden", "hidden time example", option.Hidden),
|
||||
)
|
||||
|
||||
return nil
|
||||
@@ -29,6 +30,7 @@ func Args() *console.Command {
|
||||
out.Println(ctx, "bar: <info>", in.Option(ctx, "bar").String(), "</info>")
|
||||
out.Println(ctx, "cat: <info>", in.Option(ctx, "cat").String(), "</info>")
|
||||
out.Println(ctx, "time: <info>", in.Option(ctx, "time").Time().Format(time.RFC3339), "</info>")
|
||||
out.Println(ctx, "hidden: <info>", in.Option(ctx, "hidden").Time().Format(time.RFC3339), "</info>")
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
12
help.go
12
help.go
@@ -22,8 +22,8 @@ func init() {
|
||||
}
|
||||
|
||||
const (
|
||||
HelpArgumentCommandName = "command_name"
|
||||
helpOptFormat = "format"
|
||||
ArgumentCommandName = "command_name"
|
||||
OptionFormat = "format"
|
||||
)
|
||||
|
||||
func help() *Command {
|
||||
@@ -39,8 +39,8 @@ To display the list of available commands, please use the <info>list</info> comm
|
||||
`,
|
||||
Execute: func(ctx context.Context, in input.Input, out output.Output) error {
|
||||
var err error
|
||||
name := in.Argument(ctx, HelpArgumentCommandName).String()
|
||||
format := in.Option(ctx, helpOptFormat).String()
|
||||
name := in.Argument(ctx, ArgumentCommandName).String()
|
||||
format := in.Option(ctx, OptionFormat).String()
|
||||
|
||||
des, err := descriptor.Find(format)
|
||||
if err != nil {
|
||||
@@ -81,10 +81,10 @@ To display the list of available commands, please use the <info>list</info> comm
|
||||
formats := descriptor.Descriptors()
|
||||
config.
|
||||
SetArguments(
|
||||
argument.String(HelpArgumentCommandName, "The command name", argument.Default(value.New("help"))),
|
||||
argument.String(ArgumentCommandName, "The command name", argument.Default(value.New("help"))),
|
||||
).
|
||||
SetOptions(
|
||||
option.String(helpOptFormat, fmt.Sprintf("The output format (%s)", strings.Join(formats, ", ")),
|
||||
option.String(OptionFormat, fmt.Sprintf("The output format (%s)", strings.Join(formats, ", ")),
|
||||
option.Required,
|
||||
option.Default(formats[0]),
|
||||
option.Valid(
|
||||
|
||||
@@ -15,6 +15,10 @@ func Default(in interface{}) variable.Option {
|
||||
return variable.Default(value.New(in))
|
||||
}
|
||||
|
||||
func Hidden(in *variable.Variable) {
|
||||
variable.Hidden(in)
|
||||
}
|
||||
|
||||
func Required(v *variable.Variable) {
|
||||
variable.Required(v)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ func Required(v *Variable) {
|
||||
v.Flag |= flag.Required
|
||||
}
|
||||
|
||||
func Hidden(v *Variable) {
|
||||
v.hidden = true
|
||||
}
|
||||
|
||||
func WithParse(create Create, update Append) Option {
|
||||
return func(v *Variable) {
|
||||
v.append = func(Param) Append { return update }
|
||||
@@ -99,6 +103,7 @@ type Variable struct {
|
||||
Flag flag.Flag
|
||||
Type ArgType
|
||||
Default value.Value
|
||||
hidden bool
|
||||
Valid []func(value.Value) error
|
||||
params Params
|
||||
create func(Param) Create
|
||||
@@ -115,6 +120,10 @@ func (v Variable) Validate(in value.Value) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v Variable) IsHidden() bool {
|
||||
return v.hidden
|
||||
}
|
||||
|
||||
func (v Variable) IsArray() bool {
|
||||
return v.Flag.IsArray()
|
||||
}
|
||||
|
||||
12
list.go
12
list.go
@@ -20,6 +20,10 @@ func init() {
|
||||
MustRegister(list())
|
||||
}
|
||||
|
||||
const (
|
||||
ArgumentNamespace = "namespace"
|
||||
)
|
||||
|
||||
func list() *Command {
|
||||
return &Command{
|
||||
Name: CommandList,
|
||||
@@ -37,10 +41,10 @@ You can also output the information in other formats by using the <comment>--for
|
||||
formats := descriptor.Descriptors()
|
||||
config.
|
||||
SetArguments(
|
||||
argument.String("namespace", "The namespace name"),
|
||||
argument.String(ArgumentNamespace, "The namespace name"),
|
||||
).
|
||||
SetOptions(
|
||||
option.String(helpOptFormat, fmt.Sprintf("The output format (%s)", strings.Join(formats, ", ")),
|
||||
option.String(OptionFormat, fmt.Sprintf("The output format (%s)", strings.Join(formats, ", ")),
|
||||
option.Required,
|
||||
option.Default(formats[0]),
|
||||
option.Valid(
|
||||
@@ -57,8 +61,8 @@ You can also output the information in other formats by using the <comment>--for
|
||||
|
||||
//nolint:cyclop
|
||||
func executeList(ctx context.Context, in input.Input, out output.Output) error {
|
||||
ns := in.Argument(ctx, "namespace").String()
|
||||
format := in.Option(ctx, helpOptFormat).String()
|
||||
ns := in.Argument(ctx, ArgumentNamespace).String()
|
||||
format := in.Option(ctx, OptionFormat).String()
|
||||
|
||||
des, err := descriptor.Find(format)
|
||||
if err != nil {
|
||||
|
||||
@@ -209,6 +209,9 @@ func txtDefinitionOption(maxLen int, def *input.Definition) string {
|
||||
|
||||
for _, name := range opts {
|
||||
opt, _ := def.Option(name)
|
||||
if opt.IsHidden() {
|
||||
continue
|
||||
}
|
||||
|
||||
var op bytes.Buffer
|
||||
|
||||
|
||||
30
register.go
30
register.go
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -14,12 +13,6 @@ const (
|
||||
CommandList = "list"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("command not found")
|
||||
ErrCommandNil = errors.New("console: Register command is nil")
|
||||
ErrCommandDuplicate = errors.New("console: duplicate command")
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
commandsMu sync.RWMutex
|
||||
@@ -27,27 +20,6 @@ var (
|
||||
findCommand = regexp.MustCompile("([^:]+|)")
|
||||
)
|
||||
|
||||
type AlternativesError struct {
|
||||
alt []string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e AlternativesError) Error() string {
|
||||
return fmt.Sprintf("%s, alternatives: [%s]", e.err, strings.Join(e.alt, ","))
|
||||
}
|
||||
|
||||
func (e AlternativesError) Is(err error) bool {
|
||||
return errors.Is(e.err, err)
|
||||
}
|
||||
|
||||
func (e AlternativesError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e AlternativesError) Alternatives() []string {
|
||||
return e.alt
|
||||
}
|
||||
|
||||
// MustRegister register command or panic if err.
|
||||
func MustRegister(cmd *Command) {
|
||||
if err := Register(cmd); err != nil {
|
||||
@@ -134,7 +106,7 @@ func Find(name string) (*Command, error) {
|
||||
names[i] = findCommands[i].Name
|
||||
}
|
||||
|
||||
return nil, AlternativesError{alt: names, err: ErrNotFound}
|
||||
return nil, AlternativesError{Alt: names, Err: ErrNotFound}
|
||||
}
|
||||
|
||||
return nil, ErrNotFound
|
||||
|
||||
Reference in New Issue
Block a user