add value vithh error
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing

This commit is contained in:
andrey1s
2022-09-18 21:26:55 +03:00
parent 2657672288
commit 94f5fe85c6
72 changed files with 2585 additions and 938 deletions

View File

@@ -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,
}
for _, opt := range opts {
opt(&a)
}
return a
func Default(in interface{}) variable.Option {
return variable.Default(value.New(in))
}
type Argument struct {
Name string
Description string
Default value.Value
Flag flag.Flag
Valid []func(value.Value) error
func Required(v *variable.Variable) {
variable.Required(v)
}
func (a Argument) HasDefault() bool {
return a.Default != nil
func Array(v *variable.Variable) {
variable.Array(v)
}
func (a Argument) IsBool() bool {
return a.Flag.IsBool()
func String(name, description string, opts ...variable.Option) variable.Variable {
return variable.String(name, description, append(opts, variable.ArgArgument)...)
}
func (a Argument) IsRequired() bool {
return a.Flag.IsRequired()
func Bool(name, description string, opts ...variable.Option) variable.Variable {
return variable.Bool(name, description, append(opts, variable.ArgArgument)...)
}
func (a Argument) IsArray() bool {
return a.Flag.IsArray()
func Duration(name, description string, opts ...variable.Option) variable.Variable {
return variable.Duration(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)
}
}
return nil
func Float64(name, description string, opts ...variable.Option) variable.Variable {
return variable.Float64(name, description, append(opts, variable.ArgArgument)...)
}
func Int(name, description string, opts ...variable.Option) variable.Variable {
return variable.Int(name, description, append(opts, variable.ArgArgument)...)
}
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)...)
}
func Err(name string, err error) variable.Error {
return variable.Err(name, variable.TypeArgument, err)
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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",
}
}

View File

@@ -71,6 +71,7 @@ func (i Flag) IsAny() bool {
return i&Any > 0
}
//nolint:cyclop
func (i Flag) Type() Flag {
switch {
case i.IsInt():

View File

@@ -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
}
return m.opts[name].Append(val)
}
func (m *Map) AppendArgument(f flag.Flag, name, val string) error {
if _, ok := m.args[name]; !ok {
m.SetArgument(name, value.ByFlag(f))
value, err := opt.Append(old, val)
if err != nil {
return fmt.Errorf("append option:%w", err)
}
return m.args[name].Append(val)
m.SetOption(opt.Name, value)
return nil
}
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)
}
m.SetArgument(arg.Name, value)
return nil
}

View File

@@ -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))...)
}

View File

@@ -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)
}
}
return nil
func Uint64(name, descriontion string, opts ...variable.Option) variable.Variable {
return variable.Uint64(name, descriontion, append(opts, variable.ArgOption)...)
}
func Err(name string, err error) variable.Error {
return variable.Err(name, variable.TypeOption, err)
}

View File

@@ -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)
}
}

View File

@@ -9,6 +9,8 @@ import (
)
func TestEnum(t *testing.T) {
t.Parallel()
validValue := value.New("valid")
invalidValue := value.New("invalid")

View File

@@ -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

View File

@@ -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)
}
}
}

View File

@@ -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"})

View File

@@ -1,21 +1,137 @@
package value
import "gitoa.ru/go-4devs/console/input/value/flag"
import (
"encoding/json"
"fmt"
"time"
)
type Any struct {
empty
Val []interface{}
Flag flag.Flag
var _ Value = NewAny(nil)
//nolint:gochecknoglobals
var (
emptyValue = NewAny(nil)
)
func Empty() Value {
return emptyValue
}
func (a *Any) Any() interface{} {
if a.Flag.IsArray() {
return a.Val
func IsEmpty(v Value) bool {
return v == nil || v == emptyValue
}
func NewAny(in interface{}) Value {
return Read{Any{v: in}}
}
type Any struct {
v interface{}
}
func (a Any) Any() interface{} {
return a.v
}
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)
}

View File

@@ -1,44 +1,148 @@
package value
import (
"strconv"
"gitoa.ru/go-4devs/console/input/value/flag"
"fmt"
"time"
)
type Bool struct {
empty
Val []bool
Flag flag.Flag
var (
_ ParseValue = Bool(false)
_ SliceValue = Bools{}
)
func NewBools(in []bool) Slice {
return Slice{SliceValue: Bools(in)}
}
func (b *Bool) Append(in string) error {
v, err := strconv.ParseBool(in)
if err != nil {
return err
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)
}
b.Val = append(b.Val, v)
*v = b
return nil
}
func (b *Bool) Bool() bool {
if !b.Flag.IsArray() && len(b.Val) == 1 {
return b.Val[0]
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 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)
}
return false
*v = bool(b)
return nil
}
func (b *Bool) Bools() []bool {
return b.Val
func (b Bool) ParseString() (string, error) {
return fmt.Sprintf("%v", b), nil
}
func (b *Bool) Any() interface{} {
if b.Flag.IsArray() {
return b.Bools()
func (b Bool) ParseInt() (int, error) {
if b {
return 1, nil
}
return b.Bool()
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 0, nil
}
func (b Bool) ParseUint64() (uint64, error) {
if b {
return 1, nil
}
return 0, nil
}
func (b Bool) ParseFloat64() (float64, error) {
if b {
return 1, nil
}
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)
}

View File

@@ -1,44 +1,128 @@
package value
import (
"fmt"
"time"
"gitoa.ru/go-4devs/console/input/value/flag"
)
type Duration struct {
empty
Val []time.Duration
Flag flag.Flag
var (
_ ParseValue = Duration(0)
_ SliceValue = Durations{}
)
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 out
}
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) 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 0
*v = time.Duration(d)
return nil
}
func (d *Duration) Durations() []time.Duration {
return d.Val
}
func (d *Duration) Any() interface{} {
if d.Flag.IsArray() {
return d.Durations()
}
return d.Duration()
func (d Duration) Any() interface{} {
return time.Duration(d)
}

View File

@@ -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
}

View File

@@ -1,44 +1,128 @@
package value
import (
"strconv"
"gitoa.ru/go-4devs/console/input/value/flag"
"fmt"
"time"
)
type Float64 struct {
empty
Val []float64
Flag flag.Flag
var (
_ ParseValue = Float64(0)
_ SliceValue = Float64s{}
)
func NewFloat64s(in []float64) Slice {
return Slice{SliceValue: Float64s(in)}
}
func (f *Float64) Append(in string) error {
v, err := strconv.ParseFloat(in, 64)
if err != nil {
return err
type Float64s []float64
func (f Float64s) Any() interface{} {
return f.Float64s()
}
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
}
func (f Float64) ParseFloat64() (float64, error) {
return float64(f), nil
}
func (f Float64) ParseBool() (bool, error) {
return false, fmt.Errorf("float64:%w", ErrWrongType)
}
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 0
}
*v = float64(f)
func (f *Float64) Float64s() []float64 {
return f.Val
}
func (f *Float64) Any() interface{} {
if f.Flag.IsArray() {
return f.Float64s()
}
return f.Float64()
return nil
}

View File

@@ -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())
}

View File

@@ -1,44 +1,129 @@
package value
import (
"fmt"
"strconv"
"gitoa.ru/go-4devs/console/input/value/flag"
"time"
)
type Int struct {
empty
Val []int
Flag flag.Flag
var (
_ ParseValue = Int(0)
_ SliceValue = Ints{}
)
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
}
func (i Ints) Times() []time.Time {
return nil
}
func NewInt(in int) Read {
return Read{ParseValue: Int(in)}
}
type Int int
func (i Int) Unmarshal(in interface{}) error {
v, ok := in.(*int)
if !ok {
return fmt.Errorf("%w: expect *int", ErrWrongType)
}
return 0
*v = int(i)
return nil
}
func (i *Int) Ints() []int {
return i.Val
func (i Int) ParseString() (string, error) {
return strconv.Itoa(int(i)), nil
}
func (i *Int) Any() interface{} {
if i.Flag.IsArray() {
return i.Ints()
}
return i.Int()
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)
}

View File

@@ -1,44 +1,129 @@
package value
import (
"fmt"
"strconv"
"gitoa.ru/go-4devs/console/input/value/flag"
"time"
)
type Int64 struct {
empty
Val []int64
Flag flag.Flag
var (
_ ParseValue = Int64(0)
_ SliceValue = Int64s{}
)
func NewInt64s(in []int64) Slice {
return Slice{SliceValue: Int64s(in)}
}
func (i *Int64) Int64() int64 {
if !i.Flag.IsArray() && len(i.Val) == 1 {
return i.Val[0]
type Int64s []int64
func (i Int64s) Any() interface{} {
return i.Int64s()
}
func (i Int64s) Unmarshal(val interface{}) error {
v, ok := val.(*[]int64)
if !ok {
return fmt.Errorf("%w: expect *[]int64", ErrWrongType)
}
return 0
}
func (i *Int64) Int64s() []int64 {
return i.Val
}
func (i *Int64) Any() interface{} {
if i.Flag.IsArray() {
return i.Int64s()
}
return i.Int64()
}
func (i *Int64) Append(in string) error {
v, err := strconv.ParseInt(in, 10, 64)
if err != nil {
return err
}
i.Val = append(i.Val, v)
*v = i
return nil
}
func (i Int64s) Strings() []string {
return nil
}
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
}
func (i Int64) ParseTime() (time.Time, error) {
return time.Unix(0, int64(i)), nil
}
func (i Int64) Unmarshal(val interface{}) error {
v, ok := val.(*int64)
if !ok {
return fmt.Errorf("%w: expect *int64", ErrWrongType)
}
*v = int64(i)
return nil
}

View File

@@ -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) Append(string) error {
return ErrAppendRead
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 (s Slice) ParseTime() (time.Time, error) {
return time.Time{}, s.wrongType()
}

View File

@@ -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)}
}
func (s *String) Append(in string) error {
s.Val = append(s.Val, 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 *String) String() string {
if s.Flag.IsArray() {
return ""
}
if len(s.Val) == 1 {
return s.Val[0]
}
return ""
func (s Strings) Any() interface{} {
return s.Strings()
}
func (s *String) Strings() []string {
return s.Val
func (s Strings) Strings() []string {
out := make([]string, len(s))
copy(out, s)
return out
}
func (s *String) Any() interface{} {
if s.Flag.IsArray() {
return s.Strings()
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 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)
}
return s.String()
*v = string(s)
return nil
}
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)
}
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 uout, nil
}
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) 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 v, nil
}

View File

@@ -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)
}

View File

@@ -1,44 +1,130 @@
package value
import (
"fmt"
"time"
"gitoa.ru/go-4devs/console/input/value/flag"
)
type Time struct {
empty
Val []time.Time
Flag flag.Flag
var (
_ ParseValue = Time{time.Now()}
_ SliceValue = (Times)(nil)
)
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
}
func (t Times) Uints() []uint {
return nil
}
func (t Times) Uint64s() []uint64 {
return nil
}
func (t Times) Float64s() []float64 {
return nil
}
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 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 time.Time{}
*res = t.Time
return nil
}
func (t *Time) Times() []time.Time {
return t.Val
}
func (t *Time) Amy() interface{} {
if t.Flag.IsArray() {
return t.Times()
}
return t.Time()
func (t Time) Any() interface{} {
return t.Time
}

View File

@@ -1,44 +1,129 @@
package value
import (
"fmt"
"strconv"
"gitoa.ru/go-4devs/console/input/value/flag"
"time"
)
type Uint struct {
empty
Val []uint
Flag flag.Flag
var (
_ ParseValue = Uint(0)
_ SliceValue = (Uints)(nil)
)
func NewUints(in []uint) Slice {
return Slice{SliceValue: Uints(in)}
}
func (u *Uint) Append(in string) error {
v, err := strconv.ParseUint(in, 10, 64)
if err != nil {
return err
type Uints []uint
func (u Uints) Any() interface{} {
return u.Uints()
}
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
}
func (u Uint) ParseBool() (bool, error) {
return false, fmt.Errorf("uint:%w", ErrWrongType)
}
func (u Uint) ParseDuration() (time.Duration, error) {
return time.Duration(u), nil
}
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 0
*res = uint(u)
return nil
}
func (u *Uint) Uints() []uint {
return u.Val
}
func (u *Uint) Any() interface{} {
if u.Flag.IsArray() {
return u.Uints()
}
return u.Uint()
func (u Uint) Any() interface{} {
return uint(u)
}

View File

@@ -1,44 +1,129 @@
package value
import (
"fmt"
"strconv"
"gitoa.ru/go-4devs/console/input/value/flag"
"time"
)
type Uint64 struct {
empty
Val []uint64
Flag flag.Flag
var (
_ ParseValue = Uint64(0)
_ SliceValue = (Uint64s)(nil)
)
func NewUint64s(in []uint64) Slice {
return Slice{SliceValue: Uint64s(in)}
}
func (u *Uint64) Append(in string) error {
v, err := strconv.ParseUint(in, 10, 64)
if err != nil {
return err
type Uint64s []uint64
func (u Uint64s) Any() interface{} {
return u.Uint64s()
}
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
}
func (u Uint64) ParseBool() (bool, error) {
return false, fmt.Errorf("uint64 bool:%w", ErrWrongType)
}
func (u Uint64) ParseDuration() (time.Duration, error) {
return time.Duration(u), nil
}
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 0
*res = uint64(u)
return nil
}
func (u *Uint64) Uint64s() []uint64 {
return u.Val
}
func (u *Uint64) Any() interface{} {
if u.Flag.IsArray() {
return u.Uint64s()
}
return u.Uint64()
func (u Uint64) Any() interface{} {
return uint64(u)
}

View File

@@ -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
Any() interface{}
}
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) {
case string:
return &String{Val: []string{val}, Flag: flag.String}
case int:
return &Int{Val: []int{val}, Flag: flag.Int}
case int64:
return &Int64{Val: []int64{val}, Flag: flag.Int64}
case uint:
return &Uint{Val: []uint{val}, Flag: flag.Uint}
case uint64:
return &Uint64{Val: []uint64{val}, Flag: flag.Uint64}
case float64:
return &Float64{Val: []float64{val}, Flag: flag.Float64}
//nolint:gocyclo,cyclop
func New(in interface{}) Value {
switch val := in.(type) {
case bool:
return &Bool{Val: []bool{val}, Flag: flag.Bool}
case time.Duration:
return &Duration{Val: []time.Duration{val}, Flag: flag.Duration}
case time.Time:
return &Time{Val: []time.Time{val}, Flag: flag.Time}
case []int64:
return &Int64{Val: val, Flag: flag.Int64 | flag.Array}
case []uint:
return &Uint{Val: val, Flag: flag.Uint | flag.Array}
case []uint64:
return &Uint64{Val: val, Flag: flag.Uint64 | flag.Array}
case []float64:
return &Float64{Val: val, Flag: flag.Float64 | flag.Array}
return Read{Bool(val)}
case []bool:
return &Bool{Val: val, Flag: flag.Bool | flag.Array}
return NewBools(val)
case string:
return Read{String(val)}
case int:
return Read{Int(val)}
case int64:
return Read{Int64(val)}
case uint:
return Read{Uint(val)}
case uint64:
return Read{Uint64(val)}
case float64:
return Read{Float64(val)}
case time.Duration:
return Read{Duration(val)}
case time.Time:
return Read{Time{val}}
case []int64:
return Slice{Int64s(val)}
case []uint:
return Slice{Uints(val)}
case []uint64:
return Slice{Uint64s(val)}
case []float64:
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()
}
}

10
input/variable/argtype.go Normal file
View File

@@ -0,0 +1,10 @@
package variable
//go:generate stringer -type=ArgType -linecomment
type ArgType int
const (
TypeOption ArgType = iota + 1 // option
TypeArgument // argument
)

View File

@@ -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]]
}

31
input/variable/bool.go Normal file
View File

@@ -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
}

View File

@@ -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
}

32
input/variable/err.go Normal file
View File

@@ -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,
}
}

31
input/variable/float64.go Normal file
View File

@@ -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
}

31
input/variable/int.go Normal file
View File

@@ -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
}

31
input/variable/int64.go Normal file
View File

@@ -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
}

17
input/variable/string.go Normal file
View File

@@ -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
}

77
input/variable/time.go Normal file
View File

@@ -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
}
}

31
input/variable/uint.go Normal file
View File

@@ -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
}

31
input/variable/uint64.go Normal file
View File

@@ -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
}

158
input/variable/variable.go Normal file
View File

@@ -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)
}