diff --git a/.drone.yml b/.drone.yml
index 745ce29..97b9960 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -3,7 +3,7 @@ name: default
steps:
- name: golangci-lint
- image: golangci/golangci-lint:v1.26
+ image: golangci/golangci-lint:v1.49
volumes:
- name: deps
path: /go/src/mod
diff --git a/.golangci.yml b/.golangci.yml
index f504a9e..fa1a32c 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -16,6 +16,9 @@ linters-settings:
mnd:
# don't include the "operation" and "assign"
checks: argument,case,condition,return
+ ignored-functions:
+ - "strconv.*"
+ - "strings.*"
govet:
check-shadowing: true
lll:
@@ -24,9 +27,27 @@ linters-settings:
suggest-new: true
misspell:
locale: US
+ varnamelen:
+ min-name-length: 2
+ staticcheck:
+ checks: ["all","-SA1030"]
linters:
enable-all: true
+ disable:
+ - varcheck
+ - maligned
+ - scopelint
+ - nosnakecase
+ - ifshort
+ - golint
+ - interfacer
+ - structcheck
+ - deadcode
+ - exhaustivestruct
+
+ - ireturn
+ - exhaustruct
issues:
# Excluding configuration per-path, per-linter, per-text and per-source
@@ -34,3 +55,7 @@ issues:
- path: _test\.go
linters:
- gomnd
+ - varnamelen
+ - path: input/variable
+ linters:
+ - dupl
diff --git a/app.go b/app.go
index d56af9e..1d24234 100644
--- a/app.go
+++ b/app.go
@@ -25,9 +25,7 @@ func WithInput(in input.Input) func(*App) {
// WithSkipArgs sets how many arguments are passed. For example, you don't need to pass the name of a single command.
func WithSkipArgs(l int) func(*App) {
- return func(a *App) {
- a.skipArgv = l
- }
+ return WithInput(input.NewArgs(l))
}
// WithExit sets exit callback by default os.Exit.
@@ -39,44 +37,25 @@ func WithExit(f func(int)) func(*App) {
// New creates and configure new console app.
func New(opts ...func(*App)) *App {
- a := &App{
+ app := &App{
out: output.Stdout(),
exit: os.Exit,
+ in: input.NewArgs(0),
}
for _, opt := range opts {
- opt(a)
- }
-
- if a.in == nil {
- skip := 2
-
- switch {
- case a.skipArgv > 0 && len(os.Args) > a.skipArgv:
- skip = a.skipArgv
- case a.skipArgv > 0:
- skip = len(os.Args)
- case len(os.Args) == 1:
- skip = 1
- case len(os.Args) > 1 && os.Args[1][0] == '-':
- skip = 1
- }
-
- a.in = &input.Argv{
- Args: os.Args[skip:],
- }
+ opt(app)
}
- return a
+ return app
}
// App is collection of command and configure env.
type App struct {
- cmds []*Command
- out output.Output
- in input.Input
- skipArgv int
- exit func(int)
+ cmds []*Command
+ out output.Output
+ in input.Input
+ exit func(int)
}
// Add add or replace command.
diff --git a/app_test.go b/app_test.go
index 6148c73..9e88c35 100644
--- a/app_test.go
+++ b/app_test.go
@@ -5,10 +5,9 @@ import (
"os"
"gitoa.ru/go-4devs/console"
- "gitoa.ru/go-4devs/console/example/pkg/command"
)
-//nolint: lll
+//nolint:lll
func ExampleNew_help() {
ctx := context.Background()
os.Args = []string{
@@ -56,9 +55,18 @@ func ExampleNew_list() {
console.New(console.WithExit(func(int) {})).
Add(
Command(),
- command.Hello(),
- command.Args(),
- command.Namespace(),
+ &console.Command{
+ Name: "fdevs:console:arg",
+ Description: "Understanding how Console Arguments and Options Are Handled",
+ },
+ &console.Command{
+ Name: "fdevs:console:hello",
+ Description: "example hello command",
+ },
+ &console.Command{
+ Name: "app:start",
+ Description: "example command in other namespace",
+ },
).
Execute(ctx)
// Output:
diff --git a/command.go b/command.go
index 03abb77..10d66b1 100644
--- a/command.go
+++ b/command.go
@@ -17,38 +17,38 @@ type (
)
// WithPrepare append middleware for configuration command.
-func WithPrepare(p ...Prepare) Option {
+func WithPrepare(prepares ...Prepare) Option {
return func(c *Command) {
if c.Prepare != nil {
- p = append([]Prepare{c.Prepare}, p...)
+ prepares = append([]Prepare{c.Prepare}, prepares...)
}
- c.Prepare = ChainPrepare(p...)
+ c.Prepare = ChainPrepare(prepares...)
}
}
// WithHandle append middleware for executed command.
-func WithHandle(h ...Handle) Option {
+func WithHandle(handles ...Handle) Option {
return func(c *Command) {
if c.Handle != nil {
- h = append([]Handle{c.Handle}, h...)
+ handles = append([]Handle{c.Handle}, handles...)
}
- c.Handle = ChainHandle(h...)
+ c.Handle = ChainHandle(handles...)
}
}
// WithHidden sets hidden command.
-func WithHidden(v bool) Option {
+func WithHidden(hidden bool) Option {
return func(c *Command) {
- c.Hidden = v
+ c.Hidden = hidden
}
}
// WithName sets name command.
-func WithName(n string) Option {
+func WithName(name string) Option {
return func(c *Command) {
- c.Name = n
+ c.Name = name
}
}
@@ -125,13 +125,13 @@ func (c *Command) Init(ctx context.Context, cfg *input.Definition) error {
// ChainPrepare creates middleware for configures command.
func ChainPrepare(prepare ...Prepare) Prepare {
- n := len(prepare)
- if n == 1 {
+ num := len(prepare)
+ if num == 1 {
return prepare[0]
}
- if n > 1 {
- lastI := n - 1
+ if num > 1 {
+ lastI := num - 1
return func(ctx context.Context, def *input.Definition, next Configure) error {
var (
@@ -161,13 +161,13 @@ func ChainPrepare(prepare ...Prepare) Prepare {
// ChainHandle creates middleware for executes command.
func ChainHandle(handlers ...Handle) Handle {
- n := len(handlers)
- if n == 1 {
+ num := len(handlers)
+ if num == 1 {
return handlers[0]
}
- if n > 1 {
- lastI := n - 1
+ if num > 1 {
+ lastI := num - 1
return func(ctx context.Context, in input.Input, out output.Output, next Action) error {
var (
diff --git a/command_test.go b/command_test.go
index 500a2c2..91e4207 100644
--- a/command_test.go
+++ b/command_test.go
@@ -8,17 +8,16 @@ import (
"time"
"gitoa.ru/go-4devs/console"
- "gitoa.ru/go-4devs/console/example/pkg/command"
"gitoa.ru/go-4devs/console/input"
"gitoa.ru/go-4devs/console/input/argument"
"gitoa.ru/go-4devs/console/input/option"
"gitoa.ru/go-4devs/console/output"
)
-//nolint: gochecknoinits
+//nolint:gochecknoinits
func init() {
console.MustRegister(Command().With(console.WithName("fdevs:console:test")))
- console.MustRegister(command.Args())
+ console.MustRegister(Command().With(console.WithName("fdevs:console:arg")))
}
func Command() *console.Command {
@@ -38,10 +37,10 @@ func Command() *console.Command {
Configure: func(ctx context.Context, def *input.Definition) error {
def.
SetArguments(
- argument.New("test_argument", "test argument"),
+ argument.String("test_argument", "test argument"),
).
SetOptions(
- option.New("string", "array string", option.Array),
+ option.String("string", "array string", option.Array),
option.Bool("bool", "test bool option"),
option.Duration("duration", "test duration with default", option.Default(time.Second)),
)
@@ -52,6 +51,8 @@ func Command() *console.Command {
}
func TestChainPrepare(t *testing.T) {
+ t.Parallel()
+
var cnt int32
ctx := context.Background()
@@ -86,10 +87,14 @@ func TestChainPrepare(t *testing.T) {
}
func TestChainHandle(t *testing.T) {
+ t.Parallel()
+
var cnt int32
ctx := context.Background()
- in := &input.Array{}
+ in := &input.Array{
+ Map: input.Map{},
+ }
out := output.Stdout()
handle := func(ctx context.Context, in input.Input, out output.Output, next console.Action) error {
diff --git a/console.go b/console.go
index 11d289f..a42346c 100644
--- a/console.go
+++ b/console.go
@@ -86,14 +86,14 @@ func verbose(ctx context.Context, in input.Input, out output.Output) output.Outp
case in.Option(ctx, "quiet").Bool():
out = output.Quiet()
default:
- v := in.Option(ctx, "verbose").Bools()
+ verb := in.Option(ctx, "verbose").Bools()
switch {
- case len(v) == verboseInfo:
+ case len(verb) == verboseInfo:
out = output.Verbosity(out, verbosity.Info)
- case len(v) == verboseDebug:
+ case len(verb) == verboseDebug:
out = output.Verbosity(out, verbosity.Debug)
- case len(v) >= verboseTrace:
+ case len(verb) >= verboseTrace:
out = output.Verbosity(out, verbosity.Trace)
default:
out = output.Verbosity(out, verbosity.Norm)
@@ -104,9 +104,9 @@ 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 {
- a := &input.Array{}
- a.SetArgument(HelpArgumentCommandName, value.New(cmd.Name))
- a.SetOption("help", value.New(false))
+ arr := &input.Array{}
+ arr.SetArgument(HelpArgumentCommandName, value.New(cmd.Name))
+ arr.SetOption("help", value.New(false))
if _, err := Find(cmd.Name); errors.Is(err, ErrNotFound) {
register(cmd)
@@ -117,7 +117,7 @@ func showHelp(ctx context.Context, cmd *Command, in input.Input, out output.Outp
return err
}
- w := input.Chain(a, in)
+ w := input.Chain(arr, in)
return Run(ctx, help, w, out)
}
@@ -127,11 +127,11 @@ 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("version", "Display this application version", option.Short('V')),
+ option.Bool("help", "Display this help message", option.Short('h')),
option.Bool("verbose",
"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.Short('v'), option.Array),
+ option.Bool("quiet", "Do not output any message", option.Short('q')),
)
}
diff --git a/doc.go b/doc.go
index cd64aac..5cb325e 100644
--- a/doc.go
+++ b/doc.go
@@ -2,29 +2,32 @@
// The Console package allows you to create command-line commands.
// Your console commands can be used for any recurring task, such as cronjobs, imports, or other batch jobs.
// console application can be written as follows:
-// //cmd/console/main.go
-// func main() {
-// console.New().Execute(context.Background())
-// }
+//
+// //cmd/console/main.go
+// func main() {
+// console.New().Execute(context.Background())
+// }
+//
// Then, you can register the commands using Add():
-// package main
//
-// import (
-// "context"
+// package main
+//
+// import (
+// "context"
//
-// "gitoa.ru/go-4devs/console"
-// "gitoa.ru/go-4devs/console/example/pkg/command"
-// )
+// "gitoa.ru/go-4devs/console"
+// "gitoa.ru/go-4devs/console/example/pkg/command"
+// )
//
-// func main() {
-// console.
-// New().
-// Add(
-// command.Hello(),
-// command.Args(),
-// command.Hidden(),
-// command.Namespace(),
-// ).
-// Execute(context.Background())
-// }
+// func main() {
+// console.
+// New().
+// Add(
+// command.Hello(),
+// command.Args(),
+// command.Hidden(),
+// command.Namespace(),
+// ).
+// Execute(context.Background())
+// }
package console
diff --git a/example/pkg/command/args.go b/example/pkg/command/args.go
index 3cf145a..f1f40af 100644
--- a/example/pkg/command/args.go
+++ b/example/pkg/command/args.go
@@ -2,6 +2,7 @@ package command
import (
"context"
+ "time"
"gitoa.ru/go-4devs/console"
"gitoa.ru/go-4devs/console/input"
@@ -15,9 +16,10 @@ func Args() *console.Command {
Description: "Understanding how Console Arguments and Options Are Handled",
Configure: func(ctx context.Context, def *input.Definition) error {
def.SetOptions(
- option.Bool("foo", "foo option", option.Short("f")),
- option.New("bar", "required bar option", option.Required, option.Short("b")),
- option.New("cat", "cat option", option.Short("c")),
+ option.Bool("foo", "foo option", option.Short('f')),
+ option.String("bar", "required bar option", option.Required, option.Short('b')),
+ option.String("cat", "cat option", option.Short('c')),
+ option.Time("time", "time example"),
)
return nil
@@ -26,6 +28,7 @@ func Args() *console.Command {
out.Println(ctx, "foo: ", in.Option(ctx, "foo").Bool(), "")
out.Println(ctx, "bar: ", in.Option(ctx, "bar").String(), "")
out.Println(ctx, "cat: ", in.Option(ctx, "cat").String(), "")
+ out.Println(ctx, "time: ", in.Option(ctx, "time").Time().Format(time.RFC3339), "")
return nil
},
diff --git a/example/pkg/command/create_user.go b/example/pkg/command/create_user.go
index 620e896..2a52882 100644
--- a/example/pkg/command/create_user.go
+++ b/example/pkg/command/create_user.go
@@ -6,6 +6,7 @@ import (
"gitoa.ru/go-4devs/console"
"gitoa.ru/go-4devs/console/input"
"gitoa.ru/go-4devs/console/input/argument"
+ "gitoa.ru/go-4devs/console/input/variable"
"gitoa.ru/go-4devs/console/output"
)
@@ -15,7 +16,7 @@ func CreateUser(required bool) *console.Command {
Description: "Creates a new user.",
Help: "This command allows you to create a user...",
Configure: func(ctx context.Context, cfg *input.Definition) error {
- var opts []func(*argument.Argument)
+ var opts []variable.Option
if required {
opts = append(opts, argument.Required)
}
diff --git a/example/pkg/command/create_user_test.go b/example/pkg/command/create_user_test.go
index 84b74f6..c6b051f 100644
--- a/example/pkg/command/create_user_test.go
+++ b/example/pkg/command/create_user_test.go
@@ -12,6 +12,8 @@ import (
)
func TestCreateUser(t *testing.T) {
+ t.Parallel()
+
ctx := context.Background()
buf := bytes.Buffer{}
out := output.Buffer(&buf)
diff --git a/example/pkg/command/hello.go b/example/pkg/command/hello.go
index 85948f5..26ea75c 100644
--- a/example/pkg/command/hello.go
+++ b/example/pkg/command/hello.go
@@ -25,7 +25,7 @@ func Hello() *console.Command {
},
Configure: func(_ context.Context, def *input.Definition) error {
def.SetArguments(
- argument.New("name", "Same name", argument.Default("World")),
+ argument.String("name", "Same name", argument.Default("World")),
)
return nil
diff --git a/example/pkg/command/long.go b/example/pkg/command/long.go
index a0dcd05..0b44289 100644
--- a/example/pkg/command/long.go
+++ b/example/pkg/command/long.go
@@ -6,9 +6,9 @@ import (
"gitoa.ru/go-4devs/console"
"gitoa.ru/go-4devs/console/input"
+ "gitoa.ru/go-4devs/console/input/flag"
"gitoa.ru/go-4devs/console/input/option"
"gitoa.ru/go-4devs/console/input/validator"
- "gitoa.ru/go-4devs/console/input/value/flag"
"gitoa.ru/go-4devs/console/output"
)
@@ -41,7 +41,7 @@ func Long() *console.Command {
Configure: func(ctx context.Context, def *input.Definition) error {
def.SetOptions(option.Duration("timeout", "set duration run command",
option.Default(defaultTimeout),
- option.Short("t"),
+ option.Short('t'),
option.Valid(validator.NotBlank(flag.Duration)),
))
diff --git a/go.mod b/go.mod
index 357bc26..7db15fe 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,9 @@
module gitoa.ru/go-4devs/console
go 1.15
+
+require (
+ github.com/kr/pretty v0.1.0 // indirect
+ github.com/stretchr/testify v1.8.0
+ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
+)
diff --git a/go.sum b/go.sum
index e69de29..0edc720 100644
--- a/go.sum
+++ b/go.sum
@@ -0,0 +1,21 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+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/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/help.go b/help.go
index 070c3c8..ceb9775 100644
--- a/help.go
+++ b/help.go
@@ -8,14 +8,15 @@ import (
"gitoa.ru/go-4devs/console/input"
"gitoa.ru/go-4devs/console/input/argument"
+ "gitoa.ru/go-4devs/console/input/flag"
"gitoa.ru/go-4devs/console/input/option"
"gitoa.ru/go-4devs/console/input/validator"
- "gitoa.ru/go-4devs/console/input/value/flag"
+ "gitoa.ru/go-4devs/console/input/value"
"gitoa.ru/go-4devs/console/output"
"gitoa.ru/go-4devs/console/output/descriptor"
)
-//nolint: gochecknoinits
+//nolint:gochecknoinits
func init() {
MustRegister(help())
}
@@ -43,18 +44,18 @@ To display the list of available commands, please use the list comm
des, err := descriptor.Find(format)
if err != nil {
- return err
+ return fmt.Errorf("find descriptor: %w", err)
}
cmd, err := Find(name)
if err != nil {
- return err
+ return fmt.Errorf("find cmd: %w", err)
}
def := input.NewDefinition()
if err := cmd.Init(ctx, Default(def)); err != nil {
- return err
+ return fmt.Errorf("init cmd: %w", err)
}
var bin string
@@ -62,22 +63,28 @@ To display the list of available commands, please use the list comm
bin = os.Args[0]
}
- return des.Command(ctx, out, descriptor.Command{
+ derr := des.Command(ctx, out, descriptor.Command{
Bin: bin,
Name: cmd.Name,
Description: cmd.Description,
Help: cmd.Help,
Definition: def,
})
+
+ if derr != nil {
+ return fmt.Errorf("descriptor help:%w", derr)
+ }
+
+ return nil
},
Configure: func(ctx context.Context, config *input.Definition) error {
formats := descriptor.Descriptors()
config.
SetArguments(
- argument.New(HelpArgumentCommandName, "The command name", argument.Default("help")),
+ argument.String(HelpArgumentCommandName, "The command name", argument.Default(value.New("help"))),
).
SetOptions(
- option.New(helpOptFormat, fmt.Sprintf("The output format (%s)", strings.Join(formats, ", ")),
+ option.String(helpOptFormat, fmt.Sprintf("The output format (%s)", strings.Join(formats, ", ")),
option.Required,
option.Default(formats[0]),
option.Valid(
diff --git a/input/argument/argument.go b/input/argument/argument.go
index b5f42bb..81c8f63 100644
--- a/input/argument/argument.go
+++ b/input/argument/argument.go
@@ -1,54 +1,58 @@
package argument
import (
- "gitoa.ru/go-4devs/console/input/errs"
"gitoa.ru/go-4devs/console/input/value"
- "gitoa.ru/go-4devs/console/input/value/flag"
+ "gitoa.ru/go-4devs/console/input/variable"
)
-func New(name, description string, opts ...func(*Argument)) Argument {
- a := Argument{
- Name: name,
- Description: description,
- }
+func Default(in interface{}) variable.Option {
+ return variable.Default(value.New(in))
+}
- for _, opt := range opts {
- opt(&a)
- }
+func Required(v *variable.Variable) {
+ variable.Required(v)
+}
- return a
+func Array(v *variable.Variable) {
+ variable.Array(v)
}
-type Argument struct {
- Name string
- Description string
- Default value.Value
- Flag flag.Flag
- Valid []func(value.Value) error
+func String(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.String(name, description, append(opts, variable.ArgArgument)...)
}
-func (a Argument) HasDefault() bool {
- return a.Default != nil
+func Bool(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Bool(name, description, append(opts, variable.ArgArgument)...)
}
-func (a Argument) IsBool() bool {
- return a.Flag.IsBool()
+func Duration(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Duration(name, description, append(opts, variable.ArgArgument)...)
}
-func (a Argument) IsRequired() bool {
- return a.Flag.IsRequired()
+func Float64(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Float64(name, description, append(opts, variable.ArgArgument)...)
}
-func (a Argument) IsArray() bool {
- return a.Flag.IsArray()
+func Int(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Int(name, description, append(opts, variable.ArgArgument)...)
}
-func (a Argument) Validate(v value.Value) error {
- for _, valid := range a.Valid {
- if err := valid(v); err != nil {
- return errs.Argument(a.Name, err)
- }
- }
+func Int64(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Int64(name, description, append(opts, variable.ArgArgument)...)
+}
+
+func Time(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Time(name, description, append(opts, variable.ArgArgument)...)
+}
+
+func Uint(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Uint(name, description, append(opts, variable.ArgArgument)...)
+}
+
+func Uint64(name, descriontion string, opts ...variable.Option) variable.Variable {
+ return variable.Uint64(name, descriontion, append(opts, variable.ArgArgument)...)
+}
- return nil
+func Err(name string, err error) variable.Error {
+ return variable.Err(name, variable.TypeArgument, err)
}
diff --git a/input/argument/option.go b/input/argument/option.go
deleted file mode 100644
index 4422a57..0000000
--- a/input/argument/option.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package argument
-
-import (
- "gitoa.ru/go-4devs/console/input/value"
- "gitoa.ru/go-4devs/console/input/value/flag"
-)
-
-func Required(a *Argument) {
- a.Flag |= flag.Required
-}
-
-func Default(v interface{}) func(*Argument) {
- return func(a *Argument) {
- a.Default = value.New(v)
- }
-}
-
-func Flag(flag flag.Flag) func(*Argument) {
- return func(a *Argument) {
- a.Flag = flag
- }
-}
-
-func Array(a *Argument) {
- a.Flag |= flag.Array
-}
diff --git a/input/argv.go b/input/argv.go
index d239fca..00a3438 100644
--- a/input/argv.go
+++ b/input/argv.go
@@ -3,20 +3,41 @@ package input
import (
"context"
"fmt"
+ "os"
"strings"
+ "gitoa.ru/go-4devs/console/input/argument"
"gitoa.ru/go-4devs/console/input/errs"
"gitoa.ru/go-4devs/console/input/option"
+ "gitoa.ru/go-4devs/console/input/variable"
)
const doubleDash = `--`
+func NewArgs(skip int) *Argv {
+ res := 2
+
+ switch {
+ case skip > 0 && len(os.Args) > skip:
+ res = skip
+ case skip > 0:
+ res = len(os.Args)
+ case len(os.Args) == 1:
+ res = 1
+ case len(os.Args) > 1 && os.Args[1][0] == '-':
+ res = 1
+ }
+
+ return &Argv{Args: os.Args[res:]}
+}
+
type Argv struct {
Array
Args []string
ErrHandle func(error) error
}
+//nolint:cyclop
func (i *Argv) Bind(ctx context.Context, def *Definition) error {
options := true
@@ -64,13 +85,13 @@ func (i *Argv) parseLongOption(arg string, def *Definition) error {
opt, err := def.Option(name)
if err != nil {
- return errs.Option(name, err)
+ return option.Err(name, err)
}
return i.appendOption(name, value, opt)
}
-func (i *Argv) appendOption(name string, data *string, opt option.Option) error {
+func (i *Argv) appendOption(name string, data *string, opt variable.Variable) error {
if i.HasOption(name) && !opt.IsArray() {
return fmt.Errorf("%w: got: array, expect: %s", errs.ErrUnexpectedType, opt.Flag.Type())
}
@@ -86,11 +107,11 @@ func (i *Argv) appendOption(name string, data *string, opt option.Option) error
val = i.Args[0]
i.Args = i.Args[1:]
default:
- return errs.Option(name, errs.ErrRequired)
+ return option.Err(name, errs.ErrRequired)
}
- if err := i.AppendOption(opt.Flag, name, val); err != nil {
- return errs.Option(name, err)
+ if err := i.AppendOption(opt, val); err != nil {
+ return option.Err(name, err)
}
return nil
@@ -131,8 +152,8 @@ func (i *Argv) parseArgument(arg string, def *Definition) error {
return err
}
- if err := i.AppendArgument(opt.Flag, opt.Name, arg); err != nil {
- return errs.Argument(opt.Name, err)
+ if err := i.AppendArgument(opt, arg); err != nil {
+ return argument.Err(opt.Name, err)
}
return nil
diff --git a/input/array.go b/input/array.go
index 4f8649e..16fc467 100644
--- a/input/array.go
+++ b/input/array.go
@@ -3,7 +3,9 @@ package input
import (
"context"
+ "gitoa.ru/go-4devs/console/input/argument"
"gitoa.ru/go-4devs/console/input/errs"
+ "gitoa.ru/go-4devs/console/input/option"
"gitoa.ru/go-4devs/console/input/value"
)
@@ -58,7 +60,7 @@ func (a *Array) bindOption(ctx context.Context, def *Definition) error {
continue
case opt.IsRequired():
- return errs.Option(name, errs.ErrRequired)
+ return option.Err(name, errs.ErrRequired)
default:
continue
}
@@ -70,7 +72,7 @@ func (a *Array) bindOption(ctx context.Context, def *Definition) error {
}
if err := opt.Validate(v); err != nil {
- return errs.Option(name, err)
+ return option.Err(name, err)
}
}
@@ -91,7 +93,7 @@ func (a *Array) bindArguments(ctx context.Context, def *Definition) error {
continue
case arg.IsRequired():
- return errs.Argument(name, errs.ErrRequired)
+ return argument.Err(name, errs.ErrRequired)
default:
continue
}
@@ -99,7 +101,7 @@ func (a *Array) bindArguments(ctx context.Context, def *Definition) error {
if v := a.Map.Argument(ctx, name); !value.IsEmpty(v) {
if err := arg.Validate(v); err != nil {
- return errs.Argument(name, err)
+ return argument.Err(name, err)
}
}
}
diff --git a/input/chain.go b/input/chain.go
index 6b33098..7d95a50 100644
--- a/input/chain.go
+++ b/input/chain.go
@@ -2,6 +2,7 @@ package input
import (
"context"
+ "fmt"
"gitoa.ru/go-4devs/console/input/value"
)
@@ -35,7 +36,7 @@ func (c chain) Argument(ctx context.Context, name string) value.Value {
func (c chain) Bind(ctx context.Context, def *Definition) error {
for _, input := range c {
if err := input.Bind(ctx, def); err != nil {
- return err
+ return fmt.Errorf("%T:%w", input, err)
}
}
diff --git a/input/definition.go b/input/definition.go
index 7960223..2a79d5f 100644
--- a/input/definition.go
+++ b/input/definition.go
@@ -4,20 +4,21 @@ import (
"gitoa.ru/go-4devs/console/input/argument"
"gitoa.ru/go-4devs/console/input/errs"
"gitoa.ru/go-4devs/console/input/option"
+ "gitoa.ru/go-4devs/console/input/variable"
)
func NewDefinition() *Definition {
return &Definition{
- options: make(map[string]option.Option),
- args: make(map[string]argument.Argument),
+ options: make(map[string]variable.Variable),
+ args: make(map[string]variable.Variable),
short: make(map[string]string),
}
}
type Definition struct {
- options map[string]option.Option
+ options map[string]variable.Variable
posOpt []string
- args map[string]argument.Argument
+ args map[string]variable.Variable
posArgs []string
short map[string]string
}
@@ -30,11 +31,11 @@ func (d *Definition) Arguments() []string {
return d.posArgs
}
-func (d *Definition) SetOption(name, description string, opts ...func(*option.Option)) *Definition {
- return d.SetOptions(option.New(name, description, opts...))
+func (d *Definition) SetOption(name, description string, opts ...variable.Option) *Definition {
+ return d.SetOptions(option.String(name, description, opts...))
}
-func (d *Definition) SetOptions(opts ...option.Option) *Definition {
+func (d *Definition) SetOptions(opts ...variable.Variable) *Definition {
for _, opt := range opts {
if _, has := d.options[opt.Name]; !has {
d.posOpt = append([]string{opt.Name}, d.posOpt...)
@@ -42,18 +43,18 @@ func (d *Definition) SetOptions(opts ...option.Option) *Definition {
d.options[opt.Name] = opt
if opt.HasShort() {
- d.short[opt.Short] = opt.Name
+ d.short[opt.Alias] = opt.Name
}
}
return d
}
-func (d *Definition) SetArgument(name, description string, opts ...func(*argument.Argument)) *Definition {
- return d.SetArguments(argument.New(name, description, opts...))
+func (d *Definition) SetArgument(name, description string, opts ...variable.Option) *Definition {
+ return d.SetArguments(argument.String(name, description, opts...))
}
-func (d *Definition) SetArguments(args ...argument.Argument) *Definition {
+func (d *Definition) SetArguments(args ...variable.Variable) *Definition {
for _, arg := range args {
if _, ok := d.args[arg.Name]; !ok {
d.posArgs = append(d.posArgs, arg.Name)
@@ -65,9 +66,9 @@ func (d *Definition) SetArguments(args ...argument.Argument) *Definition {
return d
}
-func (d *Definition) Argument(pos int) (argument.Argument, error) {
+func (d *Definition) Argument(pos int) (variable.Variable, error) {
if len(d.posArgs) == 0 {
- return argument.Argument{}, errs.ErrNoArgs
+ return variable.Variable{}, errs.ErrNoArgs
}
lastPos := len(d.posArgs) - 1
@@ -77,25 +78,25 @@ func (d *Definition) Argument(pos int) (argument.Argument, error) {
return arg, nil
}
- return argument.Argument{}, errs.ErrToManyArgs
+ return variable.Variable{}, errs.ErrToManyArgs
}
return d.args[d.posArgs[pos]], nil
}
-func (d *Definition) ShortOption(short string) (option.Option, error) {
+func (d *Definition) ShortOption(short string) (variable.Variable, error) {
name, ok := d.short[short]
if !ok {
- return option.Option{}, errs.ErrNotFound
+ return variable.Variable{}, errs.ErrNotFound
}
return d.Option(name)
}
-func (d *Definition) Option(name string) (option.Option, error) {
+func (d *Definition) Option(name string) (variable.Variable, error) {
if opt, ok := d.options[name]; ok {
return opt, nil
}
- return option.Option{}, errs.ErrNotFound
+ return variable.Variable{}, errs.ErrNotFound
}
diff --git a/input/errs/error.go b/input/errs/error.go
index 9d777b7..cb11876 100644
--- a/input/errs/error.go
+++ b/input/errs/error.go
@@ -2,7 +2,6 @@ package errs
import (
"errors"
- "fmt"
)
var (
@@ -13,46 +12,5 @@ var (
ErrRequired = errors.New("is required")
ErrAppend = errors.New("failed append")
ErrInvalidName = errors.New("invalid name")
+ ErrWrongType = errors.New("wrong type")
)
-
-func New(name, t string, err error) Error {
- return Error{
- name: name,
- t: t,
- err: err,
- }
-}
-
-type Error struct {
- name string
- err error
- t string
-}
-
-func (o Error) Error() string {
- return fmt.Sprintf("%s: '%s' %s", o.t, o.name, o.err)
-}
-
-func (o Error) Is(err error) bool {
- return errors.Is(err, o.err)
-}
-
-func (o Error) Unwrap() error {
- return o.err
-}
-
-func Option(name string, err error) Error {
- return Error{
- name: name,
- err: err,
- t: "option",
- }
-}
-
-func Argument(name string, err error) Error {
- return Error{
- name: name,
- err: err,
- t: "argument",
- }
-}
diff --git a/input/value/flag/flag.go b/input/flag/flag.go
similarity index 99%
rename from input/value/flag/flag.go
rename to input/flag/flag.go
index c1a2e2b..3a0812d 100644
--- a/input/value/flag/flag.go
+++ b/input/flag/flag.go
@@ -71,6 +71,7 @@ func (i Flag) IsAny() bool {
return i&Any > 0
}
+//nolint:cyclop
func (i Flag) Type() Flag {
switch {
case i.IsInt():
diff --git a/input/value/flag/flag_string.go b/input/flag/flag_string.go
similarity index 100%
rename from input/value/flag/flag_string.go
rename to input/flag/flag_string.go
diff --git a/input/map.go b/input/map.go
index 4fdcfda..9b32d4b 100644
--- a/input/map.go
+++ b/input/map.go
@@ -2,15 +2,16 @@ package input
import (
"context"
+ "fmt"
"sync"
"gitoa.ru/go-4devs/console/input/value"
- "gitoa.ru/go-4devs/console/input/value/flag"
+ "gitoa.ru/go-4devs/console/input/variable"
)
type Map struct {
- opts map[string]value.Append
- args map[string]value.Append
+ opts map[string]value.Value
+ args map[string]value.Value
sync.Mutex
}
@@ -42,15 +43,15 @@ func (m *Map) HasOption(name string) bool {
return ok
}
-func (m *Map) SetOption(name string, v interface{}) {
+func (m *Map) SetOption(name string, val interface{}) {
m.Lock()
defer m.Unlock()
if m.opts == nil {
- m.opts = make(map[string]value.Append)
+ m.opts = make(map[string]value.Value)
}
- m.opts[name] = value.New(v)
+ m.opts[name] = value.New(val)
}
func (m *Map) HasArgument(name string) bool {
@@ -59,29 +60,59 @@ func (m *Map) HasArgument(name string) bool {
return ok
}
-func (m *Map) SetArgument(name string, v interface{}) {
+func (m *Map) SetArgument(name string, val interface{}) {
m.Lock()
defer m.Unlock()
if m.args == nil {
- m.args = make(map[string]value.Append)
+ m.args = make(map[string]value.Value)
}
- m.args[name] = value.New(v)
+ m.args[name] = value.New(val)
}
-func (m *Map) AppendOption(f flag.Flag, name, val string) error {
- if _, ok := m.opts[name]; !ok {
- m.SetOption(name, value.ByFlag(f))
+func (m *Map) AppendOption(opt variable.Variable, val string) error {
+ old, ok := m.opts[opt.Name]
+ if !ok {
+ value, err := opt.Create(val)
+ if err != nil {
+ return fmt.Errorf("append option:%w", err)
+ }
+
+ m.SetOption(opt.Name, value)
+
+ return nil
+ }
+
+ value, err := opt.Append(old, val)
+ if err != nil {
+ return fmt.Errorf("append option:%w", err)
}
- return m.opts[name].Append(val)
+ m.SetOption(opt.Name, value)
+
+ return nil
}
-func (m *Map) AppendArgument(f flag.Flag, name, val string) error {
- if _, ok := m.args[name]; !ok {
- m.SetArgument(name, value.ByFlag(f))
+func (m *Map) AppendArgument(arg variable.Variable, val string) error {
+ old, ok := m.args[arg.Name]
+ if !ok {
+ value, err := arg.Create(val)
+ if err != nil {
+ return fmt.Errorf("append option:%w", err)
+ }
+
+ m.SetArgument(arg.Name, value)
+
+ return nil
+ }
+
+ value, err := arg.Append(old, val)
+ if err != nil {
+ return fmt.Errorf("append option:%w", err)
}
- return m.args[name].Append(val)
+ m.SetArgument(arg.Name, value)
+
+ return nil
}
diff --git a/input/option/helpers.go b/input/option/helpers.go
deleted file mode 100644
index 458e785..0000000
--- a/input/option/helpers.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package option
-
-import (
- "gitoa.ru/go-4devs/console/input/value/flag"
-)
-
-func Bool(name, description string, opts ...func(*Option)) Option {
- return New(name, description, append(opts, Value(flag.Bool))...)
-}
-
-func Duration(name, description string, opts ...func(*Option)) Option {
- return New(name, description, append(opts, Value(flag.Duration))...)
-}
-
-func Float64(name, description string, opts ...func(*Option)) Option {
- return New(name, description, append(opts, Value(flag.Float64))...)
-}
-
-func Int(name, description string, opts ...func(*Option)) Option {
- return New(name, description, append(opts, Value(flag.Int))...)
-}
-
-func Int64(name, description string, opts ...func(*Option)) Option {
- return New(name, description, append(opts, Value(flag.Int64))...)
-}
-
-func Time(name, description string, opts ...func(*Option)) Option {
- return New(name, description, append(opts, Value(flag.Time))...)
-}
-
-func Uint(name, description string, opts ...func(*Option)) Option {
- return New(name, description, append(opts, Value(flag.Uint))...)
-}
-
-func Uint64(name, descriontion string, opts ...func(*Option)) Option {
- return New(name, descriontion, append(opts, Value(flag.Uint64))...)
-}
diff --git a/input/option/option.go b/input/option/option.go
index a4b659e..5d749e7 100644
--- a/input/option/option.go
+++ b/input/option/option.go
@@ -1,97 +1,68 @@
package option
import (
- "gitoa.ru/go-4devs/console/input/errs"
"gitoa.ru/go-4devs/console/input/value"
- "gitoa.ru/go-4devs/console/input/value/flag"
+ "gitoa.ru/go-4devs/console/input/variable"
)
-func Required(o *Option) {
- o.Flag |= flag.Required
-}
-
-func Default(in interface{}) func(*Option) {
- return func(o *Option) {
- o.Default = value.New(in)
+func Short(in rune) variable.Option {
+ return func(v *variable.Variable) {
+ v.Alias = string(in)
}
}
-func Short(s string) func(*Option) {
- return func(o *Option) {
- o.Short = s
- }
+func Default(in interface{}) variable.Option {
+ return variable.Default(value.New(in))
}
-func Array(o *Option) {
- o.Flag |= flag.Array
+func Required(v *variable.Variable) {
+ variable.Required(v)
}
-func Value(flag flag.Flag) func(*Option) {
- return func(o *Option) {
- o.Flag |= flag
- }
+func Valid(f ...func(value.Value) error) variable.Option {
+ return variable.Valid(f...)
}
-func Flag(in flag.Flag) func(*Option) {
- return func(o *Option) {
- o.Flag = in
- }
+func Array(v *variable.Variable) {
+ variable.Array(v)
}
-func Valid(f ...func(value.Value) error) func(*Option) {
- return func(o *Option) {
- o.Valid = f
- }
+func String(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.String(name, description, append(opts, variable.ArgOption)...)
}
-func New(name, description string, opts ...func(*Option)) Option {
- o := Option{
- Name: name,
- Description: description,
- }
-
- for _, opt := range opts {
- opt(&o)
- }
-
- return o
+func Bool(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Bool(name, description, append(opts, variable.ArgOption)...)
}
-type Option struct {
- Name string
- Description string
- Short string
- Flag flag.Flag
- Default value.Value
- Valid []func(value.Value) error
+func Duration(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Duration(name, description, append(opts, variable.ArgOption)...)
}
-func (o Option) HasShort() bool {
- return len(o.Short) == 1
+func Float64(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Float64(name, description, append(opts, variable.ArgOption)...)
}
-func (o Option) HasDefault() bool {
- return o.Default != nil
+func Int(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Int(name, description, append(opts, variable.ArgOption)...)
}
-func (o Option) IsBool() bool {
- return o.Flag.IsBool()
+func Int64(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Int64(name, description, append(opts, variable.ArgOption)...)
}
-func (o Option) IsArray() bool {
- return o.Flag.IsArray()
+func Time(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Time(name, description, append(opts, variable.ArgOption)...)
}
-func (o Option) IsRequired() bool {
- return o.Flag.IsRequired()
+func Uint(name, description string, opts ...variable.Option) variable.Variable {
+ return variable.Uint(name, description, append(opts, variable.ArgOption)...)
}
-func (o Option) Validate(v value.Value) error {
- for _, valid := range o.Valid {
- if err := valid(v); err != nil {
- return errs.Option(o.Name, err)
- }
- }
+func Uint64(name, descriontion string, opts ...variable.Option) variable.Variable {
+ return variable.Uint64(name, descriontion, append(opts, variable.ArgOption)...)
+}
- return nil
+func Err(name string, err error) variable.Error {
+ return variable.Err(name, variable.TypeOption, err)
}
diff --git a/input/validator/enum.go b/input/validator/enum.go
index c6197ce..4a8b639 100644
--- a/input/validator/enum.go
+++ b/input/validator/enum.go
@@ -4,13 +4,13 @@ import "gitoa.ru/go-4devs/console/input/value"
func Enum(enum ...string) func(value.Value) error {
return func(in value.Value) error {
- v := in.String()
+ val := in.String()
for _, e := range enum {
- if e == v {
+ if e == val {
return nil
}
}
- return NewError(ErrInvalid, v, enum)
+ return NewError(ErrInvalid, val, enum)
}
}
diff --git a/input/validator/enum_test.go b/input/validator/enum_test.go
index b1844e0..c497e56 100644
--- a/input/validator/enum_test.go
+++ b/input/validator/enum_test.go
@@ -9,6 +9,8 @@ import (
)
func TestEnum(t *testing.T) {
+ t.Parallel()
+
validValue := value.New("valid")
invalidValue := value.New("invalid")
diff --git a/input/validator/not_blank.go b/input/validator/not_blank.go
index 8d9ee9d..d8d74f9 100644
--- a/input/validator/not_blank.go
+++ b/input/validator/not_blank.go
@@ -1,33 +1,33 @@
package validator
import (
+ "gitoa.ru/go-4devs/console/input/flag"
"gitoa.ru/go-4devs/console/input/value"
- "gitoa.ru/go-4devs/console/input/value/flag"
)
-//nolint: gocyclo
-func NotBlank(f flag.Flag) func(value.Value) error {
+//nolint:gocyclo,cyclop
+func NotBlank(fl flag.Flag) func(value.Value) error {
return func(in value.Value) error {
switch {
- case f.IsAny() && in.Any() != nil:
+ case fl.IsAny() && in.Any() != nil:
return nil
- case f.IsArray():
- return arrayNotBlank(f, in)
- case f.IsInt() && in.Int() != 0:
+ case fl.IsArray():
+ return arrayNotBlank(fl, in)
+ case fl.IsInt() && in.Int() != 0:
return nil
- case f.IsInt64() && in.Int64() != 0:
+ case fl.IsInt64() && in.Int64() != 0:
return nil
- case f.IsUint() && in.Uint() != 0:
+ case fl.IsUint() && in.Uint() != 0:
return nil
- case f.IsUint64() && in.Uint64() != 0:
+ case fl.IsUint64() && in.Uint64() != 0:
return nil
- case f.IsFloat64() && in.Float64() != 0:
+ case fl.IsFloat64() && in.Float64() != 0:
return nil
- case f.IsDuration() && in.Duration() != 0:
+ case fl.IsDuration() && in.Duration() != 0:
return nil
- case f.IsTime() && !in.Time().IsZero():
+ case fl.IsTime() && !in.Time().IsZero():
return nil
- case f.IsString() && len(in.String()) > 0:
+ case fl.IsString() && len(in.String()) > 0:
return nil
}
@@ -35,10 +35,10 @@ func NotBlank(f flag.Flag) func(value.Value) error {
}
}
-//nolint: gocyclo,gocognit
-func arrayNotBlank(f flag.Flag, in value.Value) error {
+//nolint:gocyclo,gocognit,cyclop
+func arrayNotBlank(fl flag.Flag, in value.Value) error {
switch {
- case f.IsInt() && len(in.Ints()) > 0:
+ case fl.IsInt() && len(in.Ints()) > 0:
for _, i := range in.Ints() {
if i == 0 {
return ErrNotBlank
@@ -46,7 +46,7 @@ func arrayNotBlank(f flag.Flag, in value.Value) error {
}
return nil
- case f.IsInt64() && len(in.Int64s()) > 0:
+ case fl.IsInt64() && len(in.Int64s()) > 0:
for _, i := range in.Int64s() {
if i == 0 {
return ErrNotBlank
@@ -54,7 +54,7 @@ func arrayNotBlank(f flag.Flag, in value.Value) error {
}
return nil
- case f.IsUint() && len(in.Uints()) > 0:
+ case fl.IsUint() && len(in.Uints()) > 0:
for _, u := range in.Uints() {
if u == 0 {
return ErrNotBlank
@@ -62,7 +62,7 @@ func arrayNotBlank(f flag.Flag, in value.Value) error {
}
return nil
- case f.IsUint64() && len(in.Uint64s()) > 0:
+ case fl.IsUint64() && len(in.Uint64s()) > 0:
for _, u := range in.Uint64s() {
if u == 0 {
return ErrNotBlank
@@ -70,7 +70,7 @@ func arrayNotBlank(f flag.Flag, in value.Value) error {
}
return nil
- case f.IsFloat64() && len(in.Float64s()) > 0:
+ case fl.IsFloat64() && len(in.Float64s()) > 0:
for _, f := range in.Float64s() {
if f == 0 {
return ErrNotBlank
@@ -78,9 +78,9 @@ func arrayNotBlank(f flag.Flag, in value.Value) error {
}
return nil
- case f.IsBool() && len(in.Bools()) > 0:
+ case fl.IsBool() && len(in.Bools()) > 0:
return nil
- case f.IsDuration() && len(in.Durations()) > 0:
+ case fl.IsDuration() && len(in.Durations()) > 0:
for _, d := range in.Durations() {
if d == 0 {
return ErrNotBlank
@@ -88,7 +88,7 @@ func arrayNotBlank(f flag.Flag, in value.Value) error {
}
return nil
- case f.IsTime() && len(in.Times()) > 0:
+ case fl.IsTime() && len(in.Times()) > 0:
for _, t := range in.Times() {
if t.IsZero() {
return ErrNotBlank
@@ -96,7 +96,7 @@ func arrayNotBlank(f flag.Flag, in value.Value) error {
}
return nil
- case f.IsString() && len(in.Strings()) > 0:
+ case fl.IsString() && len(in.Strings()) > 0:
for _, st := range in.Strings() {
if len(st) == 0 {
return ErrNotBlank
diff --git a/input/validator/not_blank_test.go b/input/validator/not_blank_test.go
index 1aca789..e74449f 100644
--- a/input/validator/not_blank_test.go
+++ b/input/validator/not_blank_test.go
@@ -5,12 +5,14 @@ import (
"testing"
"time"
+ "gitoa.ru/go-4devs/console/input/flag"
"gitoa.ru/go-4devs/console/input/validator"
"gitoa.ru/go-4devs/console/input/value"
- "gitoa.ru/go-4devs/console/input/value/flag"
)
func TestNotBlank(t *testing.T) {
+ t.Parallel()
+
cases := map[string]struct {
flag flag.Flag
value value.Value
@@ -103,7 +105,7 @@ func TestNotBlank(t *testing.T) {
}
if err := valid(ca.empty); err == nil || !errors.Is(err, validator.ErrNotBlank) {
- t.Errorf("case: %s, expect: %s, got:%s", name, validator.ErrNotBlank, err)
+ t.Errorf("case empty: %s, expect: %s, got:%s", name, validator.ErrNotBlank, err)
}
}
}
diff --git a/input/validator/valid_test.go b/input/validator/valid_test.go
index b1f8b15..e84ae1d 100644
--- a/input/validator/valid_test.go
+++ b/input/validator/valid_test.go
@@ -4,12 +4,14 @@ import (
"errors"
"testing"
+ "gitoa.ru/go-4devs/console/input/flag"
"gitoa.ru/go-4devs/console/input/validator"
"gitoa.ru/go-4devs/console/input/value"
- "gitoa.ru/go-4devs/console/input/value/flag"
)
func TestValid(t *testing.T) {
+ t.Parallel()
+
validValue := value.New("one")
invalidValue := value.New([]string{"one"})
diff --git a/input/value/any.go b/input/value/any.go
index 208bfdc..03085ce 100644
--- a/input/value/any.go
+++ b/input/value/any.go
@@ -1,21 +1,137 @@
package value
-import "gitoa.ru/go-4devs/console/input/value/flag"
+import (
+ "encoding/json"
+ "fmt"
+ "time"
+)
+
+var _ Value = NewAny(nil)
+
+//nolint:gochecknoglobals
+var (
+ emptyValue = NewAny(nil)
+)
+
+func Empty() Value {
+ return emptyValue
+}
+
+func IsEmpty(v Value) bool {
+ return v == nil || v == emptyValue
+}
+
+func NewAny(in interface{}) Value {
+ return Read{Any{v: in}}
+}
type Any struct {
- empty
- Val []interface{}
- Flag flag.Flag
+ v interface{}
+}
+
+func (a Any) Any() interface{} {
+ return a.v
}
-func (a *Any) Any() interface{} {
- if a.Flag.IsArray() {
- return a.Val
+func (a Any) Unmarshal(val interface{}) error {
+ out, err := a.ParseString()
+ if err != nil {
+ return fmt.Errorf("any parse string:%w", err)
}
- if len(a.Val) > 0 {
- return a.Val[0]
+ uerr := json.Unmarshal([]byte(out), val)
+ if uerr != nil {
+ return fmt.Errorf("any unmarshal: %w", uerr)
}
return nil
}
+
+func (a Any) ParseString() (string, error) {
+ if a.v == nil {
+ return "", nil
+ }
+
+ bout, err := json.Marshal(a.v)
+ if err != nil {
+ return "", fmt.Errorf("any string:%w", err)
+ }
+
+ return string(bout), err
+}
+
+func (a Any) ParseInt() (int, error) {
+ out, ok := a.v.(int)
+ if !ok {
+ return 0, a.wrongType("int")
+ }
+
+ return out, nil
+}
+
+func (a Any) ParseInt64() (int64, error) {
+ out, ok := a.v.(int64)
+ if !ok {
+ return 0, a.wrongType("int64")
+ }
+
+ return out, nil
+}
+
+func (a Any) ParseUint() (uint, error) {
+ out, ok := a.v.(uint)
+ if !ok {
+ return 0, a.wrongType("uint")
+ }
+
+ return out, nil
+}
+
+func (a Any) ParseUint64() (uint64, error) {
+ out, ok := a.v.(uint64)
+ if !ok {
+ return 0, a.wrongType("uint64")
+ }
+
+ return out, nil
+}
+
+func (a Any) ParseFloat64() (float64, error) {
+ out, ok := a.v.(float64)
+ if !ok {
+ return 0, a.wrongType("float64")
+ }
+
+ return out, nil
+}
+
+func (a Any) ParseBool() (bool, error) {
+ out, ok := a.v.(bool)
+ if !ok {
+ return false, a.wrongType("bool")
+ }
+
+ return out, nil
+}
+
+func (a Any) ParseDuration() (time.Duration, error) {
+ out, ok := a.v.(time.Duration)
+ if !ok {
+ return 0, a.wrongType("time.Duration")
+ }
+
+ return out, nil
+}
+
+func (a Any) ParseTime() (time.Time, error) {
+ out, ok := a.v.(time.Time)
+ if !ok {
+ return time.Time{}, a.wrongType("time.Time")
+ }
+
+ return out, nil
+}
+
+func (a Any) wrongType(ex String) error {
+ return fmt.Errorf("%w any: got: %T expect: %s", ErrWrongType, a.v, ex)
+}
diff --git a/input/value/bool.go b/input/value/bool.go
index 108d89b..0757944 100644
--- a/input/value/bool.go
+++ b/input/value/bool.go
@@ -1,44 +1,148 @@
package value
import (
- "strconv"
+ "fmt"
+ "time"
+)
- "gitoa.ru/go-4devs/console/input/value/flag"
+var (
+ _ ParseValue = Bool(false)
+ _ SliceValue = Bools{}
)
-type Bool struct {
- empty
- Val []bool
- Flag flag.Flag
+func NewBools(in []bool) Slice {
+ return Slice{SliceValue: Bools(in)}
+}
+
+type Bools []bool
+
+func (b Bools) Any() interface{} {
+ return b.Bools()
+}
+
+func (b Bools) Unmarshal(val interface{}) error {
+ v, ok := val.(*[]bool)
+ if !ok {
+ return fmt.Errorf("%w: expect: *[]bool got: %T", ErrWrongType, val)
+ }
+
+ *v = b
+
+ return nil
+}
+
+func (b Bools) Strings() []string {
+ return nil
+}
+
+func (b Bools) Ints() []int {
+ return nil
+}
+
+func (b Bools) Int64s() []int64 {
+ return nil
+}
+
+func (b Bools) Uints() []uint {
+ return nil
+}
+
+func (b Bools) Uint64s() []uint64 {
+ return nil
+}
+
+func (b Bools) Float64s() []float64 {
+ return nil
+}
+
+func (b Bools) Bools() []bool {
+ out := make([]bool, len(b))
+ copy(out, b)
+
+ return out
+}
+
+func (b Bools) Durations() []time.Duration {
+ return nil
}
-func (b *Bool) Append(in string) error {
- v, err := strconv.ParseBool(in)
- if err != nil {
- return err
+func (b Bools) Times() []time.Time {
+ return nil
+}
+
+func NewBool(in bool) Read {
+ return Read{ParseValue: Bool(in)}
+}
+
+type Bool bool
+
+func (b Bool) Unmarshal(val interface{}) error {
+ v, ok := val.(*bool)
+ if !ok {
+ return fmt.Errorf("%w: expect: *bool got: %T", ErrWrongType, val)
}
- b.Val = append(b.Val, v)
+ *v = bool(b)
return nil
}
-func (b *Bool) Bool() bool {
- if !b.Flag.IsArray() && len(b.Val) == 1 {
- return b.Val[0]
+func (b Bool) ParseString() (string, error) {
+ return fmt.Sprintf("%v", b), nil
+}
+
+func (b Bool) ParseInt() (int, error) {
+ if b {
+ return 1, nil
+ }
+
+ return 0, nil
+}
+
+func (b Bool) ParseInt64() (int64, error) {
+ if b {
+ return 1, nil
+ }
+
+ return 0, nil
+}
+
+func (b Bool) ParseUint() (uint, error) {
+ if b {
+ return 1, nil
}
- return false
+ return 0, nil
}
-func (b *Bool) Bools() []bool {
- return b.Val
+func (b Bool) ParseUint64() (uint64, error) {
+ if b {
+ return 1, nil
+ }
+
+ return 0, nil
}
-func (b *Bool) Any() interface{} {
- if b.Flag.IsArray() {
- return b.Bools()
+func (b Bool) ParseFloat64() (float64, error) {
+ if b {
+ return 1, nil
}
- return b.Bool()
+ return 0, nil
+}
+
+func (b Bool) ParseBool() (bool, error) {
+ return bool(b), nil
+}
+
+func (b Bool) ParseDuration() (time.Duration, error) {
+ return 0, fmt.Errorf("bool to duration:%w", ErrWrongType)
+}
+
+func (b Bool) ParseTime() (time.Time, error) {
+ return time.Time{}, fmt.Errorf("bool to time:%w", ErrWrongType)
+}
+
+func (b Bool) Any() interface{} {
+ return bool(b)
}
diff --git a/input/value/duration.go b/input/value/duration.go
index c712818..0d5e97f 100644
--- a/input/value/duration.go
+++ b/input/value/duration.go
@@ -1,44 +1,128 @@
package value
import (
+ "fmt"
"time"
+)
- "gitoa.ru/go-4devs/console/input/value/flag"
+var (
+ _ ParseValue = Duration(0)
+ _ SliceValue = Durations{}
)
-type Duration struct {
- empty
- Val []time.Duration
- Flag flag.Flag
+func NewDurations(in []time.Duration) Slice {
+ return Slice{SliceValue: Durations(in)}
}
-func (d *Duration) Append(in string) error {
- v, err := time.ParseDuration(in)
- if err != nil {
- return err
+type Durations []time.Duration
+
+func (d Durations) Unmarshal(val interface{}) error {
+ v, ok := val.(*[]time.Duration)
+ if !ok {
+ return fmt.Errorf("%w: expect: *[]time.Duration got: %T", ErrWrongType, val)
}
- d.Val = append(d.Val, v)
+ *v = d
return nil
}
-func (d *Duration) Duration() time.Duration {
- if !d.Flag.IsArray() && len(d.Val) == 1 {
- return d.Val[0]
- }
+func (d Durations) Any() interface{} {
+ return d.Durations()
+}
+
+func (d Durations) Strings() []string {
+ return nil
+}
+
+func (d Durations) Ints() []int {
+ return nil
+}
+
+func (d Durations) Int64s() []int64 {
+ return nil
+}
+
+func (d Durations) Uints() []uint {
+ return nil
+}
+
+func (d Durations) Uint64s() []uint64 {
+ return nil
+}
+
+func (d Durations) Float64s() []float64 {
+ return nil
+}
+
+func (d Durations) Bools() []bool {
+ return nil
+}
+
+func (d Durations) Durations() []time.Duration {
+ out := make([]time.Duration, len(d))
+ copy(out, d)
- return 0
+ return out
}
-func (d *Duration) Durations() []time.Duration {
- return d.Val
+func (d Durations) Times() []time.Time {
+ return nil
+}
+
+func NewDuration(in time.Duration) Read {
+ return Read{ParseValue: Duration(in)}
+}
+
+type Duration time.Duration
+
+func (d Duration) ParseDuration() (time.Duration, error) {
+ return time.Duration(d), nil
+}
+
+func (d Duration) ParseString() (string, error) {
+ return time.Duration(d).String(), nil
+}
+
+func (d Duration) ParseInt() (int, error) {
+ return int(d), nil
+}
+
+func (d Duration) ParseInt64() (int64, error) {
+ return int64(d), nil
+}
+
+func (d Duration) ParseUint() (uint, error) {
+ return uint(d), nil
+}
+
+func (d Duration) ParseUint64() (uint64, error) {
+ return uint64(d), nil
+}
+
+func (d Duration) ParseFloat64() (float64, error) {
+ return float64(d), nil
+}
+
+func (d Duration) ParseBool() (bool, error) {
+ return false, fmt.Errorf("duration:%w", ErrWrongType)
}
-func (d *Duration) Any() interface{} {
- if d.Flag.IsArray() {
- return d.Durations()
+func (d Duration) ParseTime() (time.Time, error) {
+ return time.Time{}, fmt.Errorf("duration:%w", ErrWrongType)
+}
+
+func (d Duration) Unmarshal(val interface{}) error {
+ v, ok := val.(*time.Duration)
+ if !ok {
+ return fmt.Errorf("%w: expect: *[]time.Duration got: %T", ErrWrongType, val)
}
- return d.Duration()
+ *v = time.Duration(d)
+
+ return nil
+}
+
+func (d Duration) Any() interface{} {
+ return time.Duration(d)
}
diff --git a/input/value/empty.go b/input/value/empty.go
deleted file mode 100644
index f1fa4ee..0000000
--- a/input/value/empty.go
+++ /dev/null
@@ -1,100 +0,0 @@
-package value
-
-import (
- "time"
-)
-
-// nolint: gochecknoglobals
-var (
- emptyValue = &empty{}
-)
-
-func Empty() Value {
- return emptyValue
-}
-
-func IsEmpty(v Value) bool {
- return v == nil || v == emptyValue
-}
-
-type empty struct{}
-
-func (e *empty) Append(string) error {
- return ErrAppendEmpty
-}
-
-func (e *empty) String() string {
- return ""
-}
-
-func (e *empty) Int() int {
- return 0
-}
-
-func (e *empty) Int64() int64 {
- return 0
-}
-
-func (e *empty) Uint() uint {
- return 0
-}
-
-func (e *empty) Uint64() uint64 {
- return 0
-}
-
-func (e *empty) Float64() float64 {
- return 0
-}
-
-func (e *empty) Bool() bool {
- return false
-}
-
-func (e *empty) Duration() time.Duration {
- return 0
-}
-
-func (e *empty) Time() time.Time {
- return time.Time{}
-}
-
-func (e *empty) Strings() []string {
- return nil
-}
-
-func (e *empty) Ints() []int {
- return nil
-}
-
-func (e *empty) Int64s() []int64 {
- return nil
-}
-
-func (e *empty) Uints() []uint {
- return nil
-}
-
-func (e *empty) Uint64s() []uint64 {
- return nil
-}
-
-func (e *empty) Float64s() []float64 {
- return nil
-}
-
-func (e *empty) Bools() []bool {
- return nil
-}
-
-func (e *empty) Durations() []time.Duration {
- return nil
-}
-
-func (e *empty) Times() []time.Time {
- return nil
-}
-
-func (e *empty) Any() interface{} {
- return nil
-}
diff --git a/input/value/float64.go b/input/value/float64.go
index e9fa6d3..af4faa5 100644
--- a/input/value/float64.go
+++ b/input/value/float64.go
@@ -1,44 +1,128 @@
package value
import (
- "strconv"
+ "fmt"
+ "time"
+)
- "gitoa.ru/go-4devs/console/input/value/flag"
+var (
+ _ ParseValue = Float64(0)
+ _ SliceValue = Float64s{}
)
-type Float64 struct {
- empty
- Val []float64
- Flag flag.Flag
+func NewFloat64s(in []float64) Slice {
+ return Slice{SliceValue: Float64s(in)}
+}
+
+type Float64s []float64
+
+func (f Float64s) Any() interface{} {
+ return f.Float64s()
}
-func (f *Float64) Append(in string) error {
- v, err := strconv.ParseFloat(in, 64)
- if err != nil {
- return err
+func (f Float64s) Unmarshal(val interface{}) error {
+ v, ok := val.(*[]float64)
+ if !ok {
+ return fmt.Errorf("%w: expect *[]float64", ErrWrongType)
}
- f.Val = append(f.Val, v)
+ *v = f
return nil
}
-func (f *Float64) Float64() float64 {
- if !f.Flag.IsArray() && len(f.Val) == 1 {
- return f.Val[0]
- }
+func (f Float64s) Strings() []string {
+ return nil
+}
+
+func (f Float64s) Ints() []int {
+ return nil
+}
+
+func (f Float64s) Int64s() []int64 {
+ return nil
+}
+
+func (f Float64s) Uints() []uint {
+ return nil
+}
+
+func (f Float64s) Uint64s() []uint64 {
+ return nil
+}
+
+func (f Float64s) Float64s() []float64 {
+ out := make([]float64, len(f))
+ copy(out, f)
+
+ return out
+}
+
+func (f Float64s) Bools() []bool {
+ return nil
+}
+
+func (f Float64s) Durations() []time.Duration {
+ return nil
+}
+
+func (f Float64s) Times() []time.Time {
+ return nil
+}
+
+func NewFloat64(in float64) Read {
+ return Read{ParseValue: Float64(in)}
+}
+
+type Float64 float64
+
+func (f Float64) Any() interface{} {
+ return float64(f)
+}
+
+func (f Float64) ParseString() (string, error) {
+ return fmt.Sprint(float64(f)), nil
+}
+
+func (f Float64) ParseInt() (int, error) {
+ return int(f), nil
+}
+
+func (f Float64) ParseInt64() (int64, error) {
+ return int64(f), nil
+}
+
+func (f Float64) ParseUint() (uint, error) {
+ return uint(f), nil
+}
+
+func (f Float64) ParseUint64() (uint64, error) {
+ return uint64(f), nil
+}
- return 0
+func (f Float64) ParseFloat64() (float64, error) {
+ return float64(f), nil
}
-func (f *Float64) Float64s() []float64 {
- return f.Val
+func (f Float64) ParseBool() (bool, error) {
+ return false, fmt.Errorf("float64:%w", ErrWrongType)
}
-func (f *Float64) Any() interface{} {
- if f.Flag.IsArray() {
- return f.Float64s()
+func (f Float64) ParseDuration() (time.Duration, error) {
+ return time.Duration(f), nil
+}
+
+func (f Float64) ParseTime() (time.Time, error) {
+ return time.Unix(0, int64(f*Float64(time.Second))), nil
+}
+
+func (f Float64) Unmarshal(in interface{}) error {
+ v, ok := in.(*float64)
+ if !ok {
+ return fmt.Errorf("%w: expect *float64", ErrWrongType)
}
- return f.Float64()
+ *v = float64(f)
+
+ return nil
}
diff --git a/input/value/float64_test.go b/input/value/float64_test.go
new file mode 100644
index 0000000..f119f4c
--- /dev/null
+++ b/input/value/float64_test.go
@@ -0,0 +1,47 @@
+package value_test
+
+import (
+ "math"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+func TestFloat64_Unmarshal(t *testing.T) {
+ t.Parallel()
+
+ f := value.Float64(math.Pi)
+
+ var out float64
+
+ require.NoError(t, f.Unmarshal(&out))
+ require.Equal(t, math.Pi, out)
+}
+
+func TestFloat64_Any(t *testing.T) {
+ t.Parallel()
+
+ f := value.Float64(math.Pi)
+
+ require.Equal(t, math.Pi, f.Any())
+}
+
+func TestFloat64s_Unmarshal(t *testing.T) {
+ t.Parallel()
+
+ f := value.Float64s{math.Pi, math.Sqrt2}
+
+ var out []float64
+
+ require.NoError(t, f.Unmarshal(&out))
+ require.Equal(t, []float64{math.Pi, math.Sqrt2}, out)
+}
+
+func TestFloat64s_Any(t *testing.T) {
+ t.Parallel()
+
+ f := value.Float64s{math.Pi, math.Sqrt2}
+
+ require.Equal(t, []float64{math.Pi, math.Sqrt2}, f.Any())
+}
diff --git a/input/value/int.go b/input/value/int.go
index 42eb9e2..537e604 100644
--- a/input/value/int.go
+++ b/input/value/int.go
@@ -1,44 +1,129 @@
package value
import (
+ "fmt"
"strconv"
+ "time"
+)
- "gitoa.ru/go-4devs/console/input/value/flag"
+var (
+ _ ParseValue = Int(0)
+ _ SliceValue = Ints{}
)
-type Int struct {
- empty
- Val []int
- Flag flag.Flag
+func NewInts(in []int) Slice {
+ return Slice{SliceValue: Ints(in)}
}
-func (i *Int) Append(in string) error {
- v, err := strconv.Atoi(in)
- if err != nil {
- return err
+type Ints []int
+
+func (i Ints) Unmarshal(in interface{}) error {
+ val, ok := in.(*[]int)
+ if !ok {
+ return fmt.Errorf("%w: expect *[]int", ErrWrongType)
}
- i.Val = append(i.Val, v)
+ *val = i
return nil
}
-func (i *Int) Int() int {
- if !i.Flag.IsArray() && len(i.Val) == 1 {
- return i.Val[0]
- }
+func (i Ints) Any() interface{} {
+ return i.Ints()
+}
+
+func (i Ints) Strings() []string {
+ return nil
+}
+
+func (i Ints) Ints() []int {
+ out := make([]int, len(i))
+ copy(out, i)
+
+ return out
+}
+
+func (i Ints) Int64s() []int64 {
+ return nil
+}
+
+func (i Ints) Uints() []uint {
+ return nil
+}
+
+func (i Ints) Uint64s() []uint64 {
+ return nil
+}
+
+func (i Ints) Float64s() []float64 {
+ return nil
+}
+
+func (i Ints) Bools() []bool {
+ return nil
+}
+
+func (i Ints) Durations() []time.Duration {
+ return nil
+}
- return 0
+func (i Ints) Times() []time.Time {
+ return nil
}
-func (i *Int) Ints() []int {
- return i.Val
+func NewInt(in int) Read {
+ return Read{ParseValue: Int(in)}
}
-func (i *Int) Any() interface{} {
- if i.Flag.IsArray() {
- return i.Ints()
+type Int int
+
+func (i Int) Unmarshal(in interface{}) error {
+ v, ok := in.(*int)
+ if !ok {
+ return fmt.Errorf("%w: expect *int", ErrWrongType)
}
- return i.Int()
+ *v = int(i)
+
+ return nil
+}
+
+func (i Int) ParseString() (string, error) {
+ return strconv.Itoa(int(i)), nil
+}
+
+func (i Int) ParseInt() (int, error) {
+ return int(i), nil
+}
+
+func (i Int) ParseInt64() (int64, error) {
+ return int64(i), nil
+}
+
+func (i Int) ParseUint() (uint, error) {
+ return uint(i), nil
+}
+
+func (i Int) ParseUint64() (uint64, error) {
+ return uint64(i), nil
+}
+
+func (i Int) ParseFloat64() (float64, error) {
+ return float64(i), nil
+}
+
+func (i Int) ParseBool() (bool, error) {
+ return false, fmt.Errorf("int:%w", ErrWrongType)
+}
+
+func (i Int) ParseDuration() (time.Duration, error) {
+ return time.Duration(i), nil
+}
+
+func (i Int) ParseTime() (time.Time, error) {
+ return time.Unix(0, int64(i)), nil
+}
+
+func (i Int) Any() interface{} {
+ return int(i)
}
diff --git a/input/value/int64.go b/input/value/int64.go
index 947cb77..f7c529d 100644
--- a/input/value/int64.go
+++ b/input/value/int64.go
@@ -1,44 +1,129 @@
package value
import (
+ "fmt"
"strconv"
+ "time"
+)
- "gitoa.ru/go-4devs/console/input/value/flag"
+var (
+ _ ParseValue = Int64(0)
+ _ SliceValue = Int64s{}
)
-type Int64 struct {
- empty
- Val []int64
- Flag flag.Flag
+func NewInt64s(in []int64) Slice {
+ return Slice{SliceValue: Int64s(in)}
+}
+
+type Int64s []int64
+
+func (i Int64s) Any() interface{} {
+ return i.Int64s()
}
-func (i *Int64) Int64() int64 {
- if !i.Flag.IsArray() && len(i.Val) == 1 {
- return i.Val[0]
+func (i Int64s) Unmarshal(val interface{}) error {
+ v, ok := val.(*[]int64)
+ if !ok {
+ return fmt.Errorf("%w: expect *[]int64", ErrWrongType)
}
- return 0
+ *v = i
+
+ return nil
}
-func (i *Int64) Int64s() []int64 {
- return i.Val
+func (i Int64s) Strings() []string {
+ return nil
}
-func (i *Int64) Any() interface{} {
- if i.Flag.IsArray() {
- return i.Int64s()
- }
+func (i Int64s) Ints() []int {
+ return nil
+}
+
+func (i Int64s) Int64s() []int64 {
+ out := make([]int64, len(i))
+ copy(out, i)
+
+ return out
+}
+
+func (i Int64s) Uints() []uint {
+ return nil
+}
+
+func (i Int64s) Uint64s() []uint64 {
+ return nil
+}
+
+func (i Int64s) Float64s() []float64 {
+ return nil
+}
+
+func (i Int64s) Bools() []bool {
+ return nil
+}
+
+func (i Int64s) Durations() []time.Duration {
+ return nil
+}
+
+func (i Int64s) Times() []time.Time {
+ return nil
+}
+
+func NewInt64(in int64) Read {
+ return Read{ParseValue: Int64(in)}
+}
+
+type Int64 int64
+
+func (i Int64) Any() interface{} {
+ return int64(i)
+}
+
+func (i Int64) ParseString() (string, error) {
+ return strconv.FormatInt(int64(i), 10), nil
+}
+
+func (i Int64) ParseInt() (int, error) {
+ return int(i), nil
+}
+
+func (i Int64) ParseInt64() (int64, error) {
+ return int64(i), nil
+}
+
+func (i Int64) ParseUint() (uint, error) {
+ return uint(i), nil
+}
+
+func (i Int64) ParseUint64() (uint64, error) {
+ return uint64(i), nil
+}
+
+func (i Int64) ParseFloat64() (float64, error) {
+ return float64(i), nil
+}
+
+func (i Int64) ParseBool() (bool, error) {
+ return false, fmt.Errorf("int64:%w", ErrWrongType)
+}
+
+func (i Int64) ParseDuration() (time.Duration, error) {
+ return time.Duration(i), nil
+}
- return i.Int64()
+func (i Int64) ParseTime() (time.Time, error) {
+ return time.Unix(0, int64(i)), nil
}
-func (i *Int64) Append(in string) error {
- v, err := strconv.ParseInt(in, 10, 64)
- if err != nil {
- return err
+func (i Int64) Unmarshal(val interface{}) error {
+ v, ok := val.(*int64)
+ if !ok {
+ return fmt.Errorf("%w: expect *int64", ErrWrongType)
}
- i.Val = append(i.Val, v)
+ *v = int64(i)
return nil
}
diff --git a/input/value/read.go b/input/value/read.go
index b984472..0ec4869 100644
--- a/input/value/read.go
+++ b/input/value/read.go
@@ -1,20 +1,189 @@
package value
import (
- "errors"
-)
+ "fmt"
+ "time"
-var _ Append = (*Read)(nil)
+ "gitoa.ru/go-4devs/console/input/errs"
+)
var (
- ErrAppendRead = errors.New("invalid append data to read value")
- ErrAppendEmpty = errors.New("invalid apped data to empty value")
+ _ Value = Read{}
+ _ Value = Slice{}
)
+var ErrWrongType = errs.ErrWrongType
+
type Read struct {
- Value
+ ParseValue
+}
+
+func (r Read) String() string {
+ sout, _ := r.ParseValue.ParseString()
+
+ return sout
+}
+
+func (r Read) Int() int {
+ iout, _ := r.ParseValue.ParseInt()
+
+ return iout
+}
+
+func (r Read) Int64() int64 {
+ iout, _ := r.ParseValue.ParseInt64()
+
+ return iout
+}
+
+func (r Read) Uint() uint {
+ uout, _ := r.ParseValue.ParseUint()
+
+ return uout
+}
+
+func (r Read) Uint64() uint64 {
+ uout, _ := r.ParseValue.ParseUint64()
+
+ return uout
+}
+
+func (r Read) Float64() float64 {
+ fout, _ := r.ParseValue.ParseFloat64()
+
+ return fout
+}
+
+func (r Read) Bool() bool {
+ bout, _ := r.ParseValue.ParseBool()
+
+ return bout
+}
+
+func (r Read) Duration() time.Duration {
+ dout, _ := r.ParseValue.ParseDuration()
+
+ return dout
+}
+
+func (r Read) Time() time.Time {
+ tout, _ := r.ParseValue.ParseTime()
+
+ return tout
+}
+
+func (r Read) Strings() []string {
+ return []string{r.String()}
+}
+
+func (r Read) Ints() []int {
+ return []int{r.Int()}
+}
+
+func (r Read) Int64s() []int64 {
+ return []int64{r.Int64()}
+}
+
+func (r Read) Uints() []uint {
+ return []uint{r.Uint()}
+}
+
+func (r Read) Uint64s() []uint64 {
+ return []uint64{r.Uint64()}
+}
+
+func (r Read) Float64s() []float64 {
+ return []float64{r.Float64()}
+}
+
+func (r Read) Bools() []bool {
+ return []bool{r.Bool()}
+}
+
+func (r Read) Durations() []time.Duration {
+ return []time.Duration{r.Duration()}
+}
+
+func (r Read) Times() []time.Time {
+ return []time.Time{r.Time()}
+}
+
+type Slice struct {
+ SliceValue
+}
+
+func (s Slice) String() string {
+ return ""
+}
+
+func (s Slice) Int() int {
+ return 0
+}
+
+func (s Slice) Int64() int64 {
+ return 0
+}
+
+func (s Slice) Uint() uint {
+ return 0
+}
+
+func (s Slice) Uint64() uint64 {
+ return 0
+}
+
+func (s Slice) Float64() float64 {
+ return 0
+}
+
+func (s Slice) Bool() bool {
+ return false
+}
+
+func (s Slice) Duration() time.Duration {
+ return 0
+}
+
+func (s Slice) Time() time.Time {
+ return time.Time{}
+}
+
+func (s Slice) wrongType() error {
+ return fmt.Errorf("%w: for %T", ErrWrongType, s.SliceValue)
+}
+
+func (s Slice) ParseString() (string, error) {
+ return "", s.wrongType()
+}
+
+func (s Slice) ParseInt() (int, error) {
+ return 0, s.wrongType()
+}
+
+func (s Slice) ParseInt64() (int64, error) {
+ return 0, s.wrongType()
+}
+
+func (s Slice) ParseUint() (uint, error) {
+ return 0, s.wrongType()
+}
+
+func (s Slice) ParseUint64() (uint64, error) {
+ return 0, s.wrongType()
+}
+
+func (s Slice) ParseFloat64() (float64, error) {
+ return 0, s.wrongType()
+}
+
+func (s Slice) ParseBool() (bool, error) {
+ return false, s.wrongType()
+}
+
+func (s Slice) ParseDuration() (time.Duration, error) {
+ return 0, s.wrongType()
}
-func (r *Read) Append(string) error {
- return ErrAppendRead
+func (s Slice) ParseTime() (time.Time, error) {
+ return time.Time{}, s.wrongType()
}
diff --git a/input/value/string.go b/input/value/string.go
index d54bcc3..2a9f246 100644
--- a/input/value/string.go
+++ b/input/value/string.go
@@ -1,39 +1,172 @@
package value
-import "gitoa.ru/go-4devs/console/input/value/flag"
+import (
+ "fmt"
+ "strconv"
+ "time"
+)
-type String struct {
- empty
- Val []string
- Flag flag.Flag
+var (
+ _ ParseValue = (String)("")
+ _ SliceValue = (Strings)(nil)
+)
+
+func NewStrings(in []string) Slice {
+ return Slice{SliceValue: Strings(in)}
+}
+
+type Strings []string
+
+func (s Strings) Unmarshal(in interface{}) error {
+ val, ok := in.(*[]string)
+ if !ok {
+ return fmt.Errorf("%w: expect *[]string", ErrWrongType)
+ }
+
+ *val = s
+
+ return nil
+}
+
+func (s Strings) Any() interface{} {
+ return s.Strings()
+}
+
+func (s Strings) Strings() []string {
+ out := make([]string, len(s))
+ copy(out, s)
+
+ return out
+}
+
+func (s Strings) Ints() []int {
+ return nil
+}
+
+func (s Strings) Int64s() []int64 {
+ return nil
+}
+
+func (s Strings) Uints() []uint {
+ return nil
+}
+
+func (s Strings) Uint64s() []uint64 {
+ return nil
+}
+
+func (s Strings) Float64s() []float64 {
+ return nil
+}
+
+func (s Strings) Bools() []bool {
+ return nil
+}
+
+func (s Strings) Durations() []time.Duration {
+ return nil
}
-func (s *String) Append(in string) error {
- s.Val = append(s.Val, in)
+func (s Strings) Times() []time.Time {
+ return nil
+}
+
+func NewString(in string) Value {
+ return Read{ParseValue: String(in)}
+}
+
+type String string
+
+func (s String) ParseString() (string, error) {
+ return string(s), nil
+}
+
+func (s String) Unmarshal(in interface{}) error {
+ v, ok := in.(*string)
+ if !ok {
+ return fmt.Errorf("%w: expect *string", ErrWrongType)
+ }
+
+ *v = string(s)
return nil
}
-func (s *String) String() string {
- if s.Flag.IsArray() {
- return ""
+func (s String) Any() interface{} {
+ return string(s)
+}
+
+func (s String) ParseInt() (int, error) {
+ v, err := strconv.Atoi(string(s))
+ if err != nil {
+ return 0, fmt.Errorf("string int:%w", err)
+ }
+
+ return v, nil
+}
+
+func (s String) Int64() int64 {
+ out, _ := s.ParseInt64()
+
+ return out
+}
+
+func (s String) ParseInt64() (int64, error) {
+ v, err := strconv.ParseInt(string(s), 10, 64)
+ if err != nil {
+ return 0, fmt.Errorf("string int64:%w", err)
}
- if len(s.Val) == 1 {
- return s.Val[0]
+ return v, nil
+}
+
+func (s String) ParseUint() (uint, error) {
+ uout, err := s.ParseUint64()
+
+ return uint(uout), err
+}
+
+func (s String) ParseUint64() (uint64, error) {
+ uout, err := strconv.ParseUint(string(s), 10, 64)
+ if err != nil {
+ return 0, fmt.Errorf("string uint:%w", err)
}
- return ""
+ return uout, nil
}
-func (s *String) Strings() []string {
- return s.Val
+func (s String) ParseFloat64() (float64, error) {
+ fout, err := strconv.ParseFloat(string(s), 64)
+ if err != nil {
+ return 0, fmt.Errorf("string float64:%w", err)
+ }
+
+ return fout, nil
+}
+
+func (s String) ParseBool() (bool, error) {
+ v, err := strconv.ParseBool(string(s))
+ if err != nil {
+ return false, fmt.Errorf("string bool:%w", err)
+ }
+
+ return v, nil
+}
+
+func (s String) ParseDuration() (time.Duration, error) {
+ v, err := time.ParseDuration(string(s))
+ if err != nil {
+ return 0, fmt.Errorf("string duration:%w", err)
+ }
+
+ return v, nil
}
-func (s *String) Any() interface{} {
- if s.Flag.IsArray() {
- return s.Strings()
+func (s String) ParseTime() (time.Time, error) {
+ v, err := time.Parse(time.RFC3339, string(s))
+ if err != nil {
+ return time.Time{}, fmt.Errorf("string time:%w", err)
}
- return s.String()
+ return v, nil
}
diff --git a/input/value/string_test.go b/input/value/string_test.go
new file mode 100644
index 0000000..0305770
--- /dev/null
+++ b/input/value/string_test.go
@@ -0,0 +1,28 @@
+package value_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+func TestStringUnmarshal(t *testing.T) {
+ t.Parallel()
+
+ st := value.New("test")
+ sta := value.New([]string{"test1", "test2"})
+
+ ac := ""
+ require.NoError(t, st.Unmarshal(&ac))
+ require.Equal(t, "test", ac)
+
+ aca := []string{}
+ require.NoError(t, sta.Unmarshal(&aca))
+ require.Equal(t, []string{"test1", "test2"}, aca)
+
+ require.ErrorIs(t, sta.Unmarshal(ac), value.ErrWrongType)
+ require.ErrorIs(t, sta.Unmarshal(&ac), value.ErrWrongType)
+ require.ErrorIs(t, st.Unmarshal(aca), value.ErrWrongType)
+ require.ErrorIs(t, st.Unmarshal(&aca), value.ErrWrongType)
+}
diff --git a/input/value/time.go b/input/value/time.go
index f751a33..e9b5482 100644
--- a/input/value/time.go
+++ b/input/value/time.go
@@ -1,44 +1,130 @@
package value
import (
+ "fmt"
"time"
+)
- "gitoa.ru/go-4devs/console/input/value/flag"
+var (
+ _ ParseValue = Time{time.Now()}
+ _ SliceValue = (Times)(nil)
)
-type Time struct {
- empty
- Val []time.Time
- Flag flag.Flag
+func NewTimes(in []time.Time) Slice {
+ return Slice{SliceValue: Times(in)}
}
-func (t *Time) Append(in string) error {
- v, err := time.Parse(time.RFC3339, in)
- if err != nil {
- return err
+type Times []time.Time
+
+func (t Times) Any() interface{} {
+ return t.Times()
+}
+
+func (t Times) Unmarshal(val interface{}) error {
+ res, ok := val.(*[]time.Time)
+ if !ok {
+ return fmt.Errorf("%w: expect *[]time.Time", ErrWrongType)
}
- t.Val = append(t.Val, v)
+ *res = t
return nil
}
-func (t *Time) Time() time.Time {
- if !t.Flag.IsArray() && len(t.Val) == 1 {
- return t.Val[0]
- }
+func (t Times) Strings() []string {
+ return nil
+}
+
+func (t Times) Ints() []int {
+ return nil
+}
+
+func (t Times) Int64s() []int64 {
+ return nil
+}
- return time.Time{}
+func (t Times) Uints() []uint {
+ return nil
+}
+
+func (t Times) Uint64s() []uint64 {
+ return nil
+}
+
+func (t Times) Float64s() []float64 {
+ return nil
}
-func (t *Time) Times() []time.Time {
- return t.Val
+func (t Times) Bools() []bool {
+ return nil
+}
+
+func (t Times) Durations() []time.Duration {
+ return nil
+}
+
+func (t Times) Times() []time.Time {
+ out := make([]time.Time, len(t))
+ copy(out, t)
+
+ return out
}
-func (t *Time) Amy() interface{} {
- if t.Flag.IsArray() {
- return t.Times()
+func NewTime(in time.Time) Read {
+ return Read{ParseValue: Time{Time: in}}
+}
+
+type Time struct {
+ time.Time
+}
+
+func (t Time) ParseString() (string, error) {
+ return t.Format(time.RFC3339), nil
+}
+
+func (t Time) ParseInt() (int, error) {
+ return int(t.Unix()), nil
+}
+
+func (t Time) ParseInt64() (int64, error) {
+ return t.Unix(), nil
+}
+
+func (t Time) ParseUint() (uint, error) {
+ return uint(t.Unix()), nil
+}
+
+func (t Time) ParseUint64() (uint64, error) {
+ return uint64(t.Unix()), nil
+}
+
+func (t Time) ParseFloat64() (float64, error) {
+ return float64(t.UnixNano()), nil
+}
+
+func (t Time) ParseBool() (bool, error) {
+ return false, fmt.Errorf("time bool:%w", ErrWrongType)
+}
+
+func (t Time) ParseDuration() (time.Duration, error) {
+ return 0, fmt.Errorf("time duration:%w", ErrWrongType)
+}
+
+func (t Time) ParseTime() (time.Time, error) {
+ return t.Time, nil
+}
+
+func (t Time) Unmarshal(val interface{}) error {
+ res, ok := val.(*time.Time)
+ if !ok {
+ return fmt.Errorf("%w: expect *time.Time", ErrWrongType)
}
- return t.Time()
+ *res = t.Time
+
+ return nil
+}
+
+func (t Time) Any() interface{} {
+ return t.Time
}
diff --git a/input/value/uint.go b/input/value/uint.go
index 1983b0d..25b4d76 100644
--- a/input/value/uint.go
+++ b/input/value/uint.go
@@ -1,44 +1,129 @@
package value
import (
+ "fmt"
"strconv"
+ "time"
+)
- "gitoa.ru/go-4devs/console/input/value/flag"
+var (
+ _ ParseValue = Uint(0)
+ _ SliceValue = (Uints)(nil)
)
-type Uint struct {
- empty
- Val []uint
- Flag flag.Flag
+func NewUints(in []uint) Slice {
+ return Slice{SliceValue: Uints(in)}
+}
+
+type Uints []uint
+
+func (u Uints) Any() interface{} {
+ return u.Uints()
}
-func (u *Uint) Append(in string) error {
- v, err := strconv.ParseUint(in, 10, 64)
- if err != nil {
- return err
+func (u Uints) Unmarshal(val interface{}) error {
+ res, ok := val.(*[]uint)
+ if !ok {
+ return fmt.Errorf("%w: expect *[]uint", ErrWrongType)
}
- u.Val = append(u.Val, uint(v))
+ *res = u
return nil
}
-func (u *Uint) Uint() uint {
- if !u.Flag.IsArray() && len(u.Val) == 1 {
- return u.Val[0]
- }
+func (u Uints) Strings() []string {
+ return nil
+}
+
+func (u Uints) Ints() []int {
+ return nil
+}
+
+func (u Uints) Int64s() []int64 {
+ return nil
+}
+
+func (u Uints) Uints() []uint {
+ out := make([]uint, len(u))
+ copy(out, u)
+
+ return out
+}
+
+func (u Uints) Uint64s() []uint64 {
+ return nil
+}
+
+func (u Uints) Float64s() []float64 {
+ return nil
+}
+
+func (u Uints) Bools() []bool {
+ return nil
+}
+
+func (u Uints) Durations() []time.Duration {
+ return nil
+}
+
+func (u Uints) Times() []time.Time {
+ return nil
+}
+
+func NewUint(in uint) Read {
+ return Read{ParseValue: Uint(in)}
+}
+
+type Uint uint
+
+func (u Uint) ParseString() (string, error) {
+ return strconv.FormatUint(uint64(u), 10), nil
+}
+
+func (u Uint) ParseInt() (int, error) {
+ return int(u), nil
+}
+
+func (u Uint) ParseInt64() (int64, error) {
+ return int64(u), nil
+}
+
+func (u Uint) ParseUint() (uint, error) {
+ return uint(u), nil
+}
+
+func (u Uint) ParseUint64() (uint64, error) {
+ return uint64(u), nil
+}
+
+func (u Uint) ParseFloat64() (float64, error) {
+ return float64(u), nil
+}
- return 0
+func (u Uint) ParseBool() (bool, error) {
+ return false, fmt.Errorf("uint:%w", ErrWrongType)
}
-func (u *Uint) Uints() []uint {
- return u.Val
+func (u Uint) ParseDuration() (time.Duration, error) {
+ return time.Duration(u), nil
}
-func (u *Uint) Any() interface{} {
- if u.Flag.IsArray() {
- return u.Uints()
+func (u Uint) ParseTime() (time.Time, error) {
+ return time.Unix(0, int64(u)), nil
+}
+
+func (u Uint) Unmarshal(val interface{}) error {
+ res, ok := val.(*uint)
+ if !ok {
+ return fmt.Errorf("%w: expect *uint", ErrWrongType)
}
- return u.Uint()
+ *res = uint(u)
+
+ return nil
+}
+
+func (u Uint) Any() interface{} {
+ return uint(u)
}
diff --git a/input/value/uint64.go b/input/value/uint64.go
index a78d8cf..264216a 100644
--- a/input/value/uint64.go
+++ b/input/value/uint64.go
@@ -1,44 +1,129 @@
package value
import (
+ "fmt"
"strconv"
+ "time"
+)
- "gitoa.ru/go-4devs/console/input/value/flag"
+var (
+ _ ParseValue = Uint64(0)
+ _ SliceValue = (Uint64s)(nil)
)
-type Uint64 struct {
- empty
- Val []uint64
- Flag flag.Flag
+func NewUint64s(in []uint64) Slice {
+ return Slice{SliceValue: Uint64s(in)}
+}
+
+type Uint64s []uint64
+
+func (u Uint64s) Any() interface{} {
+ return u.Uint64s()
}
-func (u *Uint64) Append(in string) error {
- v, err := strconv.ParseUint(in, 10, 64)
- if err != nil {
- return err
+func (u Uint64s) Unmarshal(val interface{}) error {
+ res, ok := val.(*[]uint64)
+ if !ok {
+ return fmt.Errorf("%w: expect *[]uint64", ErrWrongType)
}
- u.Val = append(u.Val, v)
+ *res = u
return nil
}
-func (u *Uint64) Uint64() uint64 {
- if !u.Flag.IsArray() && len(u.Val) == 1 {
- return u.Val[0]
- }
+func (u Uint64s) Strings() []string {
+ return nil
+}
+
+func (u Uint64s) Ints() []int {
+ return nil
+}
+
+func (u Uint64s) Int64s() []int64 {
+ return nil
+}
+
+func (u Uint64s) Uints() []uint {
+ return nil
+}
+
+func (u Uint64s) Uint64s() []uint64 {
+ out := make([]uint64, len(u))
+ copy(out, u)
+
+ return out
+}
+
+func (u Uint64s) Float64s() []float64 {
+ return nil
+}
+
+func (u Uint64s) Bools() []bool {
+ return nil
+}
+
+func (u Uint64s) Durations() []time.Duration {
+ return nil
+}
+
+func (u Uint64s) Times() []time.Time {
+ return nil
+}
+
+func NewUint64(in uint64) Read {
+ return Read{ParseValue: Uint64(in)}
+}
+
+type Uint64 uint64
+
+func (u Uint64) ParseString() (string, error) {
+ return strconv.FormatUint(uint64(u), 10), nil
+}
+
+func (u Uint64) ParseInt() (int, error) {
+ return int(u), nil
+}
+
+func (u Uint64) ParseInt64() (int64, error) {
+ return int64(u), nil
+}
+
+func (u Uint64) ParseUint() (uint, error) {
+ return uint(u), nil
+}
+
+func (u Uint64) ParseUint64() (uint64, error) {
+ return uint64(u), nil
+}
+
+func (u Uint64) ParseFloat64() (float64, error) {
+ return float64(u), nil
+}
- return 0
+func (u Uint64) ParseBool() (bool, error) {
+ return false, fmt.Errorf("uint64 bool:%w", ErrWrongType)
}
-func (u *Uint64) Uint64s() []uint64 {
- return u.Val
+func (u Uint64) ParseDuration() (time.Duration, error) {
+ return time.Duration(u), nil
}
-func (u *Uint64) Any() interface{} {
- if u.Flag.IsArray() {
- return u.Uint64s()
+func (u Uint64) ParseTime() (time.Time, error) {
+ return time.Unix(0, int64(0)), nil
+}
+
+func (u Uint64) Unmarshal(val interface{}) error {
+ res, ok := val.(*uint64)
+ if !ok {
+ return fmt.Errorf("%w: expect *uint64", ErrWrongType)
}
- return u.Uint64()
+ *res = uint64(u)
+
+ return nil
+}
+
+func (u Uint64) Any() interface{} {
+ return uint64(u)
}
diff --git a/input/value/value.go b/input/value/value.go
index 87d7d44..6359ef3 100644
--- a/input/value/value.go
+++ b/input/value/value.go
@@ -2,11 +2,19 @@ package value
import (
"time"
-
- "gitoa.ru/go-4devs/console/input/value/flag"
)
type Value interface {
+ ReadValue
+ ParseValue
+ ArrValue
+}
+
+type UnmarshalValue interface {
+ Unmarshal(val interface{}) error
+}
+
+type ReadValue interface {
String() string
Int() int
Int64() int64
@@ -16,8 +24,19 @@ type Value interface {
Bool() bool
Duration() time.Duration
Time() time.Time
+}
+
+type AnyValue interface {
Any() interface{}
+}
+type SliceValue interface {
+ AnyValue
+ UnmarshalValue
+ ArrValue
+}
+
+type ArrValue interface {
Strings() []string
Ints() []int
Int64s() []int64
@@ -29,86 +48,74 @@ type Value interface {
Times() []time.Time
}
+//nolint:interfacebloat
+type ParseValue interface {
+ ParseString() (string, error)
+ ParseInt() (int, error)
+ ParseInt64() (int64, error)
+ ParseUint() (uint, error)
+ ParseUint64() (uint64, error)
+ ParseFloat64() (float64, error)
+ ParseBool() (bool, error)
+ ParseDuration() (time.Duration, error)
+ ParseTime() (time.Time, error)
+ UnmarshalValue
+ AnyValue
+}
+
type Append interface {
Value
- Append(string) error
+ Append(string) (Value, error)
}
-//nolint: gocyclo
-func New(v interface{}) Append {
- switch val := v.(type) {
+//nolint:gocyclo,cyclop
+func New(in interface{}) Value {
+ switch val := in.(type) {
+ case bool:
+ return Read{Bool(val)}
+ case []bool:
+ return NewBools(val)
case string:
- return &String{Val: []string{val}, Flag: flag.String}
+ return Read{String(val)}
case int:
- return &Int{Val: []int{val}, Flag: flag.Int}
+ return Read{Int(val)}
case int64:
- return &Int64{Val: []int64{val}, Flag: flag.Int64}
+ return Read{Int64(val)}
case uint:
- return &Uint{Val: []uint{val}, Flag: flag.Uint}
+ return Read{Uint(val)}
case uint64:
- return &Uint64{Val: []uint64{val}, Flag: flag.Uint64}
+ return Read{Uint64(val)}
case float64:
- return &Float64{Val: []float64{val}, Flag: flag.Float64}
- case bool:
- return &Bool{Val: []bool{val}, Flag: flag.Bool}
+ return Read{Float64(val)}
case time.Duration:
- return &Duration{Val: []time.Duration{val}, Flag: flag.Duration}
+ return Read{Duration(val)}
case time.Time:
- return &Time{Val: []time.Time{val}, Flag: flag.Time}
+ return Read{Time{val}}
case []int64:
- return &Int64{Val: val, Flag: flag.Int64 | flag.Array}
+ return Slice{Int64s(val)}
case []uint:
- return &Uint{Val: val, Flag: flag.Uint | flag.Array}
+ return Slice{Uints(val)}
case []uint64:
- return &Uint64{Val: val, Flag: flag.Uint64 | flag.Array}
+ return Slice{Uint64s(val)}
case []float64:
- return &Float64{Val: val, Flag: flag.Float64 | flag.Array}
- case []bool:
- return &Bool{Val: val, Flag: flag.Bool | flag.Array}
+ return Slice{Float64s(val)}
case []time.Duration:
- return &Duration{Val: val, Flag: flag.Duration | flag.Array}
+ return Slice{Durations(val)}
case []time.Time:
- return &Time{Val: val, Flag: flag.Time | flag.Array}
+ return Slice{Times(val)}
case []string:
- return &String{Val: val, Flag: flag.String | flag.Array}
+ return Slice{Strings(val)}
case []int:
- return &Int{Val: val, Flag: flag.Int | flag.Array}
+ return Slice{Ints(val)}
case []interface{}:
- return &Any{Val: val, Flag: flag.Any | flag.Array}
- case Append:
- return val
+ return Read{Any{v: val}}
case Value:
- return &Read{Value: val}
+ return val
default:
- if v != nil {
- return &Any{Val: []interface{}{v}, Flag: flag.Any}
+ if in != nil {
+ return Read{Any{v: in}}
}
- return &empty{}
- }
-}
-
-func ByFlag(f flag.Flag) Append {
- switch {
- case f.IsInt():
- return &Int{Flag: f | flag.Int}
- case f.IsInt64():
- return &Int64{Flag: f | flag.Int64}
- case f.IsUint():
- return &Uint{Flag: f | flag.Uint}
- case f.IsUint64():
- return &Uint64{Flag: f | flag.Uint64}
- case f.IsFloat64():
- return &Float64{Flag: f | flag.Float64}
- case f.IsBool():
- return &Bool{Flag: f | flag.Bool}
- case f.IsDuration():
- return &Duration{Flag: f | flag.Duration}
- case f.IsTime():
- return &Time{Flag: f | flag.Time}
- case f.IsAny():
- return &Any{Flag: f | flag.Any}
- default:
- return &String{}
+ return Empty()
}
}
diff --git a/input/variable/argtype.go b/input/variable/argtype.go
new file mode 100644
index 0000000..6b9f71a
--- /dev/null
+++ b/input/variable/argtype.go
@@ -0,0 +1,10 @@
+package variable
+
+//go:generate stringer -type=ArgType -linecomment
+
+type ArgType int
+
+const (
+ TypeOption ArgType = iota + 1 // option
+ TypeArgument // argument
+)
diff --git a/input/variable/argtype_string.go b/input/variable/argtype_string.go
new file mode 100644
index 0000000..cd1c446
--- /dev/null
+++ b/input/variable/argtype_string.go
@@ -0,0 +1,25 @@
+// Code generated by "stringer -type=ArgType -linecomment"; DO NOT EDIT.
+
+package variable
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[TypeOption-1]
+ _ = x[TypeArgument-2]
+}
+
+const _ArgType_name = "optionargument"
+
+var _ArgType_index = [...]uint8{0, 6, 14}
+
+func (i ArgType) String() string {
+ i -= 1
+ if i < 0 || i >= ArgType(len(_ArgType_index)-1) {
+ return "ArgType(" + strconv.FormatInt(int64(i+1), 10) + ")"
+ }
+ return _ArgType_name[_ArgType_index[i]:_ArgType_index[i+1]]
+}
diff --git a/input/variable/bool.go b/input/variable/bool.go
new file mode 100644
index 0000000..43686be
--- /dev/null
+++ b/input/variable/bool.go
@@ -0,0 +1,31 @@
+package variable
+
+import (
+ "fmt"
+ "strconv"
+
+ "gitoa.ru/go-4devs/console/input/flag"
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+func Bool(name, description string, opts ...Option) Variable {
+ return String(name, description, append(opts, WithParse(CreateBool, AppendBool), Value(flag.Bool))...)
+}
+
+func CreateBool(in string) (value.Value, error) {
+ out, err := strconv.ParseBool(in)
+ if err != nil {
+ return nil, fmt.Errorf("create bool:%w", err)
+ }
+
+ return value.NewBool(out), nil
+}
+
+func AppendBool(old value.Value, in string) (value.Value, error) {
+ out, err := strconv.ParseBool(in)
+ if err != nil {
+ return nil, fmt.Errorf("create bool:%w", err)
+ }
+
+ return value.NewBools(append(old.Bools(), out)), nil
+}
diff --git a/input/variable/duration.go b/input/variable/duration.go
new file mode 100644
index 0000000..e51ea83
--- /dev/null
+++ b/input/variable/duration.go
@@ -0,0 +1,31 @@
+package variable
+
+import (
+ "fmt"
+ "time"
+
+ "gitoa.ru/go-4devs/console/input/flag"
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+func Duration(name, description string, opts ...Option) Variable {
+ return String(name, description, append(opts, WithParse(CreateDuration, AppendDuration), Value(flag.Duration))...)
+}
+
+func CreateDuration(in string) (value.Value, error) {
+ out, err := time.ParseDuration(in)
+ if err != nil {
+ return nil, fmt.Errorf("create duration:%w", err)
+ }
+
+ return value.NewDuration(out), nil
+}
+
+func AppendDuration(old value.Value, in string) (value.Value, error) {
+ out, err := time.ParseDuration(in)
+ if err != nil {
+ return nil, fmt.Errorf("append duration:%w", err)
+ }
+
+ return value.NewDurations(append(old.Durations(), out)), nil
+}
diff --git a/input/variable/err.go b/input/variable/err.go
new file mode 100644
index 0000000..0272df1
--- /dev/null
+++ b/input/variable/err.go
@@ -0,0 +1,32 @@
+package variable
+
+import (
+ "errors"
+ "fmt"
+)
+
+type Error struct {
+ Name string
+ Err error
+ Type ArgType
+}
+
+func (o Error) Error() string {
+ return fmt.Sprintf("%s: '%s' %s", o.Type, o.Name, o.Err)
+}
+
+func (o Error) Is(err error) bool {
+ return errors.Is(err, o.Err)
+}
+
+func (o Error) Unwrap() error {
+ return o.Err
+}
+
+func Err(name string, t ArgType, err error) Error {
+ return Error{
+ Name: name,
+ Type: t,
+ Err: err,
+ }
+}
diff --git a/input/variable/float64.go b/input/variable/float64.go
new file mode 100644
index 0000000..d19777b
--- /dev/null
+++ b/input/variable/float64.go
@@ -0,0 +1,31 @@
+package variable
+
+import (
+ "fmt"
+ "strconv"
+
+ "gitoa.ru/go-4devs/console/input/flag"
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+func Float64(name, description string, opts ...Option) Variable {
+ return String(name, description, append(opts, WithParse(CreateFloat64, AppendFloat64), Value(flag.Float64))...)
+}
+
+func CreateFloat64(in string) (value.Value, error) {
+ out, err := strconv.ParseFloat(in, 10)
+ if err != nil {
+ return nil, fmt.Errorf("create float64:%w", err)
+ }
+
+ return value.NewFloat64(out), nil
+}
+
+func AppendFloat64(old value.Value, in string) (value.Value, error) {
+ out, err := strconv.ParseFloat(in, 10)
+ if err != nil {
+ return nil, fmt.Errorf("append float64:%w", err)
+ }
+
+ return value.NewFloat64s(append(old.Float64s(), out)), nil
+}
diff --git a/input/variable/int.go b/input/variable/int.go
new file mode 100644
index 0000000..2dccb10
--- /dev/null
+++ b/input/variable/int.go
@@ -0,0 +1,31 @@
+package variable
+
+import (
+ "fmt"
+ "strconv"
+
+ "gitoa.ru/go-4devs/console/input/flag"
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+func Int(name, description string, opts ...Option) Variable {
+ return New(name, description, append(opts, WithParse(CreateInt, AppendInt), Value(flag.Int))...)
+}
+
+func AppendInt(old value.Value, in string) (value.Value, error) {
+ out, err := strconv.Atoi(in)
+ if err != nil {
+ return nil, fmt.Errorf("append int:%w", err)
+ }
+
+ return value.NewInts(append(old.Ints(), out)), nil
+}
+
+func CreateInt(in string) (value.Value, error) {
+ out, err := strconv.Atoi(in)
+ if err != nil {
+ return nil, fmt.Errorf("create int:%w", err)
+ }
+
+ return value.NewInt(out), nil
+}
diff --git a/input/variable/int64.go b/input/variable/int64.go
new file mode 100644
index 0000000..26d2d1a
--- /dev/null
+++ b/input/variable/int64.go
@@ -0,0 +1,31 @@
+package variable
+
+import (
+ "fmt"
+ "strconv"
+
+ "gitoa.ru/go-4devs/console/input/flag"
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+func Int64(name, description string, opts ...Option) Variable {
+ return String(name, description, append(opts, WithParse(CreateInt64, AppendInt64), Value(flag.Int64))...)
+}
+
+func CreateInt64(in string) (value.Value, error) {
+ out, err := strconv.ParseInt(in, 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("create int64:%w", err)
+ }
+
+ return value.NewInt64(out), nil
+}
+
+func AppendInt64(old value.Value, in string) (value.Value, error) {
+ out, err := strconv.ParseInt(in, 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("append int64:%w", err)
+ }
+
+ return value.NewInt64s(append(old.Int64s(), out)), nil
+}
diff --git a/input/variable/string.go b/input/variable/string.go
new file mode 100644
index 0000000..ccfb5d1
--- /dev/null
+++ b/input/variable/string.go
@@ -0,0 +1,17 @@
+package variable
+
+import (
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+func String(name, description string, opts ...Option) Variable {
+ return New(name, description, opts...)
+}
+
+func CreateString(in string) (value.Value, error) {
+ return value.NewString(in), nil
+}
+
+func AppendString(old value.Value, in string) (value.Value, error) {
+ return value.NewStrings(append(old.Strings(), in)), nil
+}
diff --git a/input/variable/time.go b/input/variable/time.go
new file mode 100644
index 0000000..4b04071
--- /dev/null
+++ b/input/variable/time.go
@@ -0,0 +1,77 @@
+package variable
+
+import (
+ "fmt"
+ "time"
+
+ "gitoa.ru/go-4devs/console/input/errs"
+ "gitoa.ru/go-4devs/console/input/flag"
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+const (
+ ParamFormat = "format"
+)
+
+func Time(name, description string, opts ...Option) Variable {
+ return String(name, description, append(opts,
+ WithParamParse(CreateTime, AppendTime),
+ WithParam(ParamFormat, RFC3339),
+ Value(flag.Time),
+ )...)
+}
+
+func RFC3339(in interface{}) error {
+ v, ok := in.(*string)
+ if !ok {
+ return fmt.Errorf("%w: expect *string got %T", errs.ErrWrongType, in)
+ }
+
+ *v = time.RFC3339
+
+ return nil
+}
+
+func CreateTime(param Param) Create {
+ var (
+ formatErr error
+ format string
+ )
+
+ formatErr = param.Value(ParamFormat, &format)
+
+ return func(in string) (value.Value, error) {
+ if formatErr != nil {
+ return nil, fmt.Errorf("create format:%w", formatErr)
+ }
+
+ out, err := time.Parse(format, in)
+ if err != nil {
+ return nil, fmt.Errorf("create time:%w", err)
+ }
+
+ return value.NewTime(out), nil
+ }
+}
+
+func AppendTime(param Param) Append {
+ var (
+ formatErr error
+ format string
+ )
+
+ formatErr = param.Value(ParamFormat, &format)
+
+ return func(old value.Value, in string) (value.Value, error) {
+ if formatErr != nil {
+ return nil, fmt.Errorf("append format:%w", formatErr)
+ }
+
+ out, err := time.Parse(format, in)
+ if err != nil {
+ return nil, fmt.Errorf("append time:%w", err)
+ }
+
+ return value.NewTimes(append(old.Times(), out)), nil
+ }
+}
diff --git a/input/variable/uint.go b/input/variable/uint.go
new file mode 100644
index 0000000..f6cc768
--- /dev/null
+++ b/input/variable/uint.go
@@ -0,0 +1,31 @@
+package variable
+
+import (
+ "fmt"
+ "strconv"
+
+ "gitoa.ru/go-4devs/console/input/flag"
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+func Uint(name, description string, opts ...Option) Variable {
+ return String(name, description, append(opts, WithParse(CreateUint, AppendUint), Value(flag.Uint))...)
+}
+
+func CreateUint(in string) (value.Value, error) {
+ out, err := strconv.ParseUint(in, 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("create uint:%w", err)
+ }
+
+ return value.NewUint(uint(out)), nil
+}
+
+func AppendUint(old value.Value, in string) (value.Value, error) {
+ out, err := strconv.ParseUint(in, 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("append uint:%w", err)
+ }
+
+ return value.NewUints(append(old.Uints(), uint(out))), nil
+}
diff --git a/input/variable/uint64.go b/input/variable/uint64.go
new file mode 100644
index 0000000..70dc7b2
--- /dev/null
+++ b/input/variable/uint64.go
@@ -0,0 +1,31 @@
+package variable
+
+import (
+ "fmt"
+ "strconv"
+
+ "gitoa.ru/go-4devs/console/input/flag"
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+func Uint64(name, descriontion string, opts ...Option) Variable {
+ return String(name, descriontion, append(opts, WithParse(CreateUint64, AppendUint64), Value(flag.Uint64))...)
+}
+
+func CreateUint64(in string) (value.Value, error) {
+ out, err := strconv.ParseUint(in, 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("create uint64:%w", err)
+ }
+
+ return value.NewUint64(out), nil
+}
+
+func AppendUint64(old value.Value, in string) (value.Value, error) {
+ out, err := strconv.ParseUint(in, 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("append uint64:%w", err)
+ }
+
+ return value.NewUint64s(append(old.Uint64s(), out)), nil
+}
diff --git a/input/variable/variable.go b/input/variable/variable.go
new file mode 100644
index 0000000..c3f5bf2
--- /dev/null
+++ b/input/variable/variable.go
@@ -0,0 +1,158 @@
+package variable
+
+import (
+ "fmt"
+
+ "gitoa.ru/go-4devs/console/input/errs"
+ "gitoa.ru/go-4devs/console/input/flag"
+ "gitoa.ru/go-4devs/console/input/value"
+)
+
+type Option func(*Variable)
+
+func WithType(t ArgType) Option {
+ return func(v *Variable) {
+ v.Type = t
+ }
+}
+
+func ArgOption(v *Variable) {
+ v.Type = TypeOption
+}
+
+func ArgArgument(v *Variable) {
+ v.Type = TypeArgument
+}
+
+func Value(in flag.Flag) Option {
+ return func(v *Variable) {
+ v.Flag |= in
+ }
+}
+
+func Default(in value.Value) Option {
+ return func(v *Variable) {
+ v.Default = in
+ }
+}
+
+func Required(v *Variable) {
+ v.Flag |= flag.Required
+}
+
+func WithParse(create Create, update Append) Option {
+ return func(v *Variable) {
+ v.append = func(Param) Append { return update }
+ v.create = func(Param) Create { return create }
+ }
+}
+
+func WithParamParse(create func(Param) Create, update func(Param) Append) Option {
+ return func(v *Variable) {
+ v.append = update
+ v.create = create
+ }
+}
+
+func Valid(f ...func(value.Value) error) Option {
+ return func(v *Variable) {
+ v.Valid = f
+ }
+}
+
+func Array(o *Variable) {
+ o.Flag |= flag.Array
+}
+
+func WithParam(name string, fn func(interface{}) error) Option {
+ return func(v *Variable) {
+ v.params[name] = fn
+ }
+}
+
+type (
+ Create func(s string) (value.Value, error)
+ Append func(old value.Value, s string) (value.Value, error)
+)
+
+func New(name, description string, opts ...Option) Variable {
+ res := Variable{
+ Name: name,
+ Description: description,
+ Type: TypeOption,
+ create: func(Param) Create { return CreateString },
+ append: func(Param) Append { return AppendString },
+ params: make(Params),
+ }
+
+ for _, opt := range opts {
+ opt(&res)
+ }
+
+ return res
+}
+
+type Variable struct {
+ Name string
+ Description string
+ Alias string
+ Flag flag.Flag
+ Type ArgType
+ Default value.Value
+ Valid []func(value.Value) error
+ params Params
+ create func(Param) Create
+ append func(Param) Append
+}
+
+func (v Variable) Validate(in value.Value) error {
+ for _, valid := range v.Valid {
+ if err := valid(in); err != nil {
+ return Err(v.Name, v.Type, err)
+ }
+ }
+
+ return nil
+}
+
+func (v Variable) IsArray() bool {
+ return v.Flag.IsArray()
+}
+
+func (v Variable) IsRequired() bool {
+ return v.Flag.IsRequired()
+}
+
+func (v Variable) HasDefault() bool {
+ return v.Default != nil
+}
+
+func (v Variable) IsBool() bool {
+ return v.Flag.IsBool()
+}
+
+func (v Variable) HasShort() bool {
+ return v.Type == TypeOption && len(v.Alias) == 1
+}
+
+func (v Variable) Create(s string) (value.Value, error) {
+ return v.create(v.params)(s)
+}
+
+func (v Variable) Append(old value.Value, s string) (value.Value, error) {
+ return v.append(v.params)(old, s)
+}
+
+type Param interface {
+ Value(name string, v interface{}) error
+}
+
+type Params map[string]func(interface{}) error
+
+func (p Params) Value(name string, v interface{}) error {
+ if p, ok := p[name]; ok {
+ return p(v)
+ }
+
+ return fmt.Errorf("%w: param %v", errs.ErrNotFound, name)
+}
diff --git a/list.go b/list.go
index 008dac9..9a73248 100644
--- a/list.go
+++ b/list.go
@@ -15,7 +15,7 @@ import (
const defaultLenNamespace = 2
-//nolint: gochecknoinits
+//nolint:gochecknoinits
func init() {
MustRegister(list())
}
@@ -32,71 +32,15 @@ You can also display the commands for a specific namespace:
You can also output the information in other formats by using the --format option:
{{ .Bin }} {{ .Name }} --format=xml
`,
- Execute: func(ctx context.Context, in input.Input, out output.Output) error {
- ns := in.Argument(ctx, "namespace").String()
- format := in.Option(ctx, helpOptFormat).String()
-
- des, err := descriptor.Find(format)
- if err != nil {
- return err
- }
- cmds := Commands()
- commands := descriptor.Commands{
- Namespace: ns,
- Definition: Default(input.NewDefinition()),
- }
- groups := make(map[string]*descriptor.NSCommand)
- namespaces := make([]string, 0, len(cmds))
- empty := descriptor.NSCommand{}
-
- for _, name := range cmds {
- if ns != "" && !strings.HasPrefix(name, ns+":") {
- continue
- }
-
- cmd, _ := Find(name)
- if cmd.Hidden {
- continue
- }
-
- gn := strings.SplitN(name, ":", 2)
- if len(gn) != defaultLenNamespace {
- empty.Append(cmd.Name, cmd.Description)
-
- continue
- }
-
- if _, ok := groups[gn[0]]; !ok {
- groups[gn[0]] = &descriptor.NSCommand{
- Name: gn[0],
- }
- namespaces = append(namespaces, gn[0])
- }
-
- groups[gn[0]].Append(name, cmd.Description)
- }
- if len(empty.Commands) > 0 {
- commands.Commands = append(commands.Commands, empty)
- }
-
- for _, name := range namespaces {
- commands.Commands = append(commands.Commands, *groups[name])
- }
-
- if ns != "" && len(commands.Commands) == 0 {
- return fmt.Errorf("%w: namespace %s", ErrNotFound, ns)
- }
-
- return des.Commands(ctx, out, commands)
- },
+ Execute: executeList,
Configure: func(ctx context.Context, config *input.Definition) error {
formats := descriptor.Descriptors()
config.
SetArguments(
- argument.New("namespace", "The namespace name"),
+ argument.String("namespace", "The namespace name"),
).
SetOptions(
- option.New(helpOptFormat, fmt.Sprintf("The output format (%s)", strings.Join(formats, ", ")),
+ option.String(helpOptFormat, fmt.Sprintf("The output format (%s)", strings.Join(formats, ", ")),
option.Required,
option.Default(formats[0]),
option.Valid(
@@ -110,3 +54,69 @@ You can also output the information in other formats by using the --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()
+
+ des, err := descriptor.Find(format)
+ if err != nil {
+ return fmt.Errorf("find descriptor: %w", err)
+ }
+
+ cmds := Commands()
+ commands := descriptor.Commands{
+ Namespace: ns,
+ Definition: Default(input.NewDefinition()),
+ }
+ groups := make(map[string]*descriptor.NSCommand)
+ namespaces := make([]string, 0, len(cmds))
+ empty := descriptor.NSCommand{}
+
+ for _, name := range cmds {
+ if ns != "" && !strings.HasPrefix(name, ns+":") {
+ continue
+ }
+
+ cmd, _ := Find(name)
+ if cmd.Hidden {
+ continue
+ }
+
+ gn := strings.SplitN(name, ":", 2)
+ if len(gn) != defaultLenNamespace {
+ empty.Append(cmd.Name, cmd.Description)
+
+ continue
+ }
+
+ if _, ok := groups[gn[0]]; !ok {
+ groups[gn[0]] = &descriptor.NSCommand{
+ Name: gn[0],
+ }
+
+ namespaces = append(namespaces, gn[0])
+ }
+
+ groups[gn[0]].Append(name, cmd.Description)
+ }
+
+ if len(empty.Commands) > 0 {
+ commands.Commands = append(commands.Commands, empty)
+ }
+
+ for _, name := range namespaces {
+ commands.Commands = append(commands.Commands, *groups[name])
+ }
+
+ if ns != "" && len(commands.Commands) == 0 {
+ return fmt.Errorf("%w: namespace %s", ErrNotFound, ns)
+ }
+
+ if err := des.Commands(ctx, out, commands); err != nil {
+ return fmt.Errorf("descriptor:%w", err)
+ }
+
+ return nil
+}
diff --git a/output/descriptor/descriptor.go b/output/descriptor/descriptor.go
index e5f2e38..fabef29 100644
--- a/output/descriptor/descriptor.go
+++ b/output/descriptor/descriptor.go
@@ -11,7 +11,7 @@ import (
var ErrDescriptorNotFound = errors.New("descriptor not found")
-//nolint: gochecknoglobals
+//nolint:gochecknoglobals
var (
descriptors = map[string]Descriptor{
"txt": &txt{},
diff --git a/output/descriptor/txt.go b/output/descriptor/txt.go
index f99812e..7189ee4 100644
--- a/output/descriptor/txt.go
+++ b/output/descriptor/txt.go
@@ -10,8 +10,8 @@ import (
"time"
"gitoa.ru/go-4devs/console/input"
+ "gitoa.ru/go-4devs/console/input/flag"
"gitoa.ru/go-4devs/console/input/value"
- "gitoa.ru/go-4devs/console/input/value/flag"
"gitoa.ru/go-4devs/console/output"
)
@@ -43,7 +43,7 @@ var (
{{- help . }}
`))
- txtListTempkate = template.Must(template.New("txt_list").
+ txtListTemplate = template.Must(template.New("txt_list").
Funcs(txtFunc).
Parse(`Usage:
command [options] [arguments]
@@ -58,7 +58,7 @@ func (t *txt) Command(ctx context.Context, out output.Output, cmd Command) error
var tpl bytes.Buffer
if err := txtHelpTemplate.Execute(&tpl, cmd); err != nil {
- return err
+ return fmt.Errorf("execute txt help tpl:%w", err)
}
out.Println(ctx, tpl.String())
@@ -69,8 +69,8 @@ func (t *txt) Command(ctx context.Context, out output.Output, cmd Command) error
func (t *txt) Commands(ctx context.Context, out output.Output, cmds Commands) error {
var buf bytes.Buffer
- if err := txtListTempkate.Execute(&buf, cmds); err != nil {
- return err
+ if err := txtListTemplate.Execute(&buf, cmds); err != nil {
+ return fmt.Errorf("execute txt list tpl:%w", err)
}
out.Println(ctx, buf.String())
@@ -78,36 +78,37 @@ func (t *txt) Commands(ctx context.Context, out output.Output, cmds Commands) er
return nil
}
-func txtDefaultArray(v value.Value, f flag.Flag) string {
- st := v.Strings()
+//nolint:cyclop
+func txtDefaultArray(val value.Value, fl flag.Flag) string {
+ st := val.Strings()
switch {
- case f.IsInt():
- for _, i := range v.Ints() {
+ case fl.IsInt():
+ for _, i := range val.Ints() {
st = append(st, strconv.Itoa(i))
}
- case f.IsInt64():
- for _, i := range v.Int64s() {
+ case fl.IsInt64():
+ for _, i := range val.Int64s() {
st = append(st, strconv.FormatInt(i, 10))
}
- case f.IsUint():
- for _, u := range v.Uints() {
+ case fl.IsUint():
+ for _, u := range val.Uints() {
st = append(st, strconv.FormatUint(uint64(u), 10))
}
- case f.IsUint64():
- for _, u := range v.Uint64s() {
+ case fl.IsUint64():
+ for _, u := range val.Uint64s() {
st = append(st, strconv.FormatUint(u, 10))
}
- case f.IsFloat64():
- for _, f := range v.Float64s() {
+ case fl.IsFloat64():
+ for _, f := range val.Float64s() {
st = append(st, strconv.FormatFloat(f, 'g', -1, 64))
}
- case f.IsDuration():
- for _, d := range v.Durations() {
+ case fl.IsDuration():
+ for _, d := range val.Durations() {
st = append(st, d.String())
}
- case f.IsTime():
- for _, d := range v.Times() {
+ case fl.IsTime():
+ for _, d := range val.Times() {
st = append(st, d.Format(time.RFC3339))
}
}
@@ -115,32 +116,33 @@ func txtDefaultArray(v value.Value, f flag.Flag) string {
return strings.Join(st, ",")
}
-func txtDefault(v value.Value, f flag.Flag) []byte {
+//nolint:cyclop
+func txtDefault(val value.Value, fl flag.Flag) []byte {
var buf bytes.Buffer
buf.WriteString(" [default: ")
switch {
- case f.IsArray():
- buf.WriteString(txtDefaultArray(v, f))
- case f.IsInt():
- buf.WriteString(strconv.Itoa(v.Int()))
- case f.IsInt64():
- buf.WriteString(strconv.FormatInt(v.Int64(), 10))
- case f.IsUint():
- buf.WriteString(strconv.FormatUint(uint64(v.Uint()), 10))
- case f.IsUint64():
- buf.WriteString(strconv.FormatUint(v.Uint64(), 10))
- case f.IsFloat64():
- buf.WriteString(strconv.FormatFloat(v.Float64(), 'g', -1, 64))
- case f.IsDuration():
- buf.WriteString(v.Duration().String())
- case f.IsTime():
- buf.WriteString(v.Time().Format(time.RFC3339))
- case f.IsAny():
- buf.WriteString(fmt.Sprint(v.Any()))
+ case fl.IsArray():
+ buf.WriteString(txtDefaultArray(val, fl))
+ case fl.IsInt():
+ buf.WriteString(strconv.Itoa(val.Int()))
+ case fl.IsInt64():
+ buf.WriteString(strconv.FormatInt(val.Int64(), 10))
+ case fl.IsUint():
+ buf.WriteString(strconv.FormatUint(uint64(val.Uint()), 10))
+ case fl.IsUint64():
+ buf.WriteString(strconv.FormatUint(val.Uint64(), 10))
+ case fl.IsFloat64():
+ buf.WriteString(strconv.FormatFloat(val.Float64(), 'g', -1, 64))
+ case fl.IsDuration():
+ buf.WriteString(val.Duration().String())
+ case fl.IsTime():
+ buf.WriteString(val.Time().Format(time.RFC3339))
+ case fl.IsAny():
+ buf.WriteString(fmt.Sprint(val.Any()))
default:
- buf.WriteString(v.String())
+ buf.WriteString(val.String())
}
buf.WriteString("]")
@@ -214,7 +216,7 @@ func txtDefinitionOption(maxLen int, def *input.Definition) string {
if opt.HasShort() {
op.WriteString("-")
- op.WriteString(opt.Short)
+ op.WriteString(opt.Alias)
op.WriteString(", ")
} else {
op.WriteString(" ")
@@ -354,18 +356,18 @@ func totalWidth(def *input.Definition) int {
for _, name := range def.Options() {
opt, _ := def.Option(name)
- l := len(opt.Name) + 6
+ current := len(opt.Name) + 6
if !opt.IsBool() {
- l = l*2 + 1
+ current = current*2 + 1
}
if opt.HasDefault() {
- l += 2
+ current += 2
}
- if l > max {
- max = l
+ if current > max {
+ max = current
}
}
diff --git a/output/formatter/formatter.go b/output/formatter/formatter.go
index 6993ed3..a2d6d98 100644
--- a/output/formatter/formatter.go
+++ b/output/formatter/formatter.go
@@ -8,7 +8,6 @@ import (
"gitoa.ru/go-4devs/console/output/style"
)
-//nolint: gochecknoglobals
var re = regexp.MustCompile(`<(([a-z][^<>]+)|/([a-z][^<>]+)?)>`)
func WithStyle(styles func(string) (style.Style, error)) func(*Formatter) {
@@ -18,15 +17,15 @@ func WithStyle(styles func(string) (style.Style, error)) func(*Formatter) {
}
func New(opts ...func(*Formatter)) *Formatter {
- f := &Formatter{
+ formatter := &Formatter{
styles: style.Find,
}
for _, opt := range opts {
- opt(f)
+ opt(formatter)
}
- return f
+ return formatter
}
type Formatter struct {
diff --git a/output/formatter/formatter_test.go b/output/formatter/formatter_test.go
index 6c87150..3c5720a 100644
--- a/output/formatter/formatter_test.go
+++ b/output/formatter/formatter_test.go
@@ -8,6 +8,8 @@ import (
)
func TestFormatter(t *testing.T) {
+ t.Parallel()
+
ctx := context.Background()
formatter := formatter.New()
diff --git a/output/formatter/none_test.go b/output/formatter/none_test.go
index b9614d7..5a0c4d6 100644
--- a/output/formatter/none_test.go
+++ b/output/formatter/none_test.go
@@ -8,6 +8,8 @@ import (
)
func TestNone(t *testing.T) {
+ t.Parallel()
+
ctx := context.Background()
none := formatter.None()
diff --git a/output/output.go b/output/output.go
index 56f61ac..3ba6284 100644
--- a/output/output.go
+++ b/output/output.go
@@ -63,15 +63,13 @@ func (o Output) Write(b []byte) (int, error) {
}
func (o Output) Writer(ctx context.Context, verb verbosity.Verbosity) io.Writer {
- return verbosityWriter{ctx, o, verb}
+ return verbosityWriter(func(b []byte) (int, error) {
+ return o(ctx, verb, string(b))
+ })
}
-type verbosityWriter struct {
- ctx context.Context
- out Output
- verb verbosity.Verbosity
-}
+type verbosityWriter func(b []byte) (int, error)
func (w verbosityWriter) Write(b []byte) (int, error) {
- return w.out(w.ctx, w.verb, string(b))
+ return w(b)
}
diff --git a/output/style/color.go b/output/style/color.go
index bb1c0b3..a5e98a2 100644
--- a/output/style/color.go
+++ b/output/style/color.go
@@ -28,13 +28,13 @@ const (
type Option string
func (o Option) Apply(action int) string {
- v := string(o)
+ out := string(o)
switch action {
case ActionSet:
- return v[0:1]
+ return out[0:1]
case ActionUnset:
- return v[1:]
+ return out[1:]
}
return ""
diff --git a/output/style/style.go b/output/style/style.go
index 91a653e..b6f6c99 100644
--- a/output/style/style.go
+++ b/output/style/style.go
@@ -7,7 +7,7 @@ import (
"sync"
)
-//nolint: gochecknoglobals
+//nolint:gochecknoglobals
var (
styles = map[string]Style{
"error": {Foreground: White, Background: Red},
diff --git a/output/writer.go b/output/writer.go
index f7b3589..b620138 100644
--- a/output/writer.go
+++ b/output/writer.go
@@ -41,6 +41,11 @@ func FormatString(_ verbosity.Verbosity, msg string, kv ...label.KeyValue) strin
func New(w io.Writer, format func(verb verbosity.Verbosity, msg string, kv ...label.KeyValue) string) Output {
return func(ctx context.Context, verb verbosity.Verbosity, msg string, kv ...label.KeyValue) (int, error) {
- return fmt.Fprint(w, format(verb, msg, kv...))
+ out, err := fmt.Fprint(w, format(verb, msg, kv...))
+ if err != nil {
+ return 0, fmt.Errorf("writer fprint:%w", err)
+ }
+
+ return out, nil
}
}
diff --git a/output/writer_test.go b/output/writer_test.go
index 931c394..6ead6e1 100644
--- a/output/writer_test.go
+++ b/output/writer_test.go
@@ -10,6 +10,8 @@ import (
)
func TestNew(t *testing.T) {
+ t.Parallel()
+
ctx := context.Background()
buf := bytes.Buffer{}
wr := output.New(&buf, output.FormatString)
diff --git a/register.go b/register.go
index bd27c59..8cddd5d 100644
--- a/register.go
+++ b/register.go
@@ -20,31 +20,31 @@ var (
ErrCommandDuplicate = errors.New("console: duplicate command")
)
-//nolint: gochecknoglobals
+//nolint:gochecknoglobals
var (
commandsMu sync.RWMutex
commands = make(map[string]*Command)
findCommand = regexp.MustCompile("([^:]+|)")
)
-type ErrorAlternatives struct {
+type AlternativesError struct {
alt []string
err error
}
-func (e ErrorAlternatives) Error() string {
+func (e AlternativesError) Error() string {
return fmt.Sprintf("%s, alternatives: [%s]", e.err, strings.Join(e.alt, ","))
}
-func (e ErrorAlternatives) Is(err error) bool {
+func (e AlternativesError) Is(err error) bool {
return errors.Is(e.err, err)
}
-func (e ErrorAlternatives) Unwrap() error {
+func (e AlternativesError) Unwrap() error {
return e.err
}
-func (e ErrorAlternatives) Alternatives() []string {
+func (e AlternativesError) Alternatives() []string {
return e.alt
}
@@ -115,7 +115,7 @@ func Find(name string) (*Command, error) {
cmdRegexp, err := regexp.Compile("^" + nameRegexp + "$")
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("find by regexp:%w", err)
}
for name := range commands {
@@ -134,7 +134,7 @@ func Find(name string) (*Command, error) {
names[i] = findCommands[i].Name
}
- return nil, ErrorAlternatives{alt: names, err: ErrNotFound}
+ return nil, AlternativesError{alt: names, err: ErrNotFound}
}
return nil, ErrNotFound
diff --git a/register_test.go b/register_test.go
index ead6fde..6176b8e 100644
--- a/register_test.go
+++ b/register_test.go
@@ -7,6 +7,8 @@ import (
)
func TestFind(t *testing.T) {
+ t.Parallel()
+
cases := map[string]string{
"fdevs:console:test": "fdevs:console:test",
"fd:c:t": "fdevs:console:test",
@@ -18,13 +20,13 @@ func TestFind(t *testing.T) {
for name, ex := range cases {
res, err := console.Find(name)
if err != nil {
- t.Errorf("expect err, got:%s", err)
+ t.Errorf("%v expect err, got:%s", name, err)
continue
}
if res.Name != ex {
- t.Errorf("expect: %s, got: %s", ex, res)
+ t.Errorf("%v expect: %s, got: %s", name, ex, res)
}
}
}