19
validator/enum.go
Normal file
19
validator/enum.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"gitoa.ru/go-4devs/config"
|
||||
"gitoa.ru/go-4devs/config/param"
|
||||
)
|
||||
|
||||
func Enum(enum ...string) Validator {
|
||||
return func(_ param.Params, in config.Value) error {
|
||||
val := in.String()
|
||||
if slices.Contains(enum, val) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NewError(ErrInvalid, val, enum)
|
||||
}
|
||||
}
|
||||
30
validator/enum_test.go
Normal file
30
validator/enum_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package validator_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"gitoa.ru/go-4devs/config/definition/option"
|
||||
"gitoa.ru/go-4devs/config/validator"
|
||||
"gitoa.ru/go-4devs/config/value"
|
||||
)
|
||||
|
||||
func TestEnum(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
vars := option.String("test", "test")
|
||||
validValue := value.New("valid")
|
||||
invalidValue := value.New("invalid")
|
||||
|
||||
enum := validator.Enum("valid", "other", "three")
|
||||
|
||||
err := enum(vars, validValue)
|
||||
if err != nil {
|
||||
t.Errorf("expected valid value got err:%s", err)
|
||||
}
|
||||
|
||||
iErr := enum(vars, invalidValue)
|
||||
if !errors.Is(iErr, validator.ErrInvalid) {
|
||||
t.Errorf("expected err:%s, got: %s", validator.ErrInvalid, iErr)
|
||||
}
|
||||
}
|
||||
37
validator/error.go
Normal file
37
validator/error.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalid = errors.New("invalid value")
|
||||
ErrNotBlank = errors.New("not blank")
|
||||
)
|
||||
|
||||
func NewError(err error, value, expect any) Error {
|
||||
return Error{
|
||||
err: err,
|
||||
value: value,
|
||||
expect: expect,
|
||||
}
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
err error
|
||||
value any
|
||||
expect any
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("%s: expext: %s, given: %s", e.err, e.expect, e.value)
|
||||
}
|
||||
|
||||
func (e Error) Is(err error) bool {
|
||||
return errors.Is(e.err, err)
|
||||
}
|
||||
|
||||
func (e Error) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
84
validator/not_blank.go
Normal file
84
validator/not_blank.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitoa.ru/go-4devs/config"
|
||||
"gitoa.ru/go-4devs/config/definition/option"
|
||||
"gitoa.ru/go-4devs/config/param"
|
||||
)
|
||||
|
||||
func NotBlank(fn param.Params, in config.Value) error {
|
||||
dataType := param.Type(fn)
|
||||
if option.IsSlice(fn) {
|
||||
return sliceByType(dataType, in)
|
||||
}
|
||||
|
||||
if notBlank(dataType, in) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrNotBlank
|
||||
}
|
||||
|
||||
func sliceByType(vType any, in config.Value) error {
|
||||
switch vType.(type) {
|
||||
case string:
|
||||
return sliceBy[string](in)
|
||||
case int:
|
||||
return sliceBy[int](in)
|
||||
case int64:
|
||||
return sliceBy[int64](in)
|
||||
case uint:
|
||||
return sliceBy[uint](in)
|
||||
case uint64:
|
||||
return sliceBy[uint64](in)
|
||||
case float64:
|
||||
return sliceBy[float64](in)
|
||||
case bool:
|
||||
return sliceBy[bool](in)
|
||||
case time.Duration:
|
||||
return sliceBy[time.Duration](in)
|
||||
case time.Time:
|
||||
return sliceBy[time.Time](in)
|
||||
default:
|
||||
return sliceBy[any](in)
|
||||
}
|
||||
}
|
||||
|
||||
func sliceBy[T any](in config.Value) error {
|
||||
var data []T
|
||||
if err := in.Unmarshal(&data); err != nil {
|
||||
return fmt.Errorf("%w:%w", ErrNotBlank, err)
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("%w", ErrNotBlank)
|
||||
}
|
||||
|
||||
func notBlank(vType any, in config.Value) bool {
|
||||
switch vType.(type) {
|
||||
case int:
|
||||
return in.Int() != 0
|
||||
case int64:
|
||||
return in.Int64() != 0
|
||||
case uint:
|
||||
return in.Uint() != 0
|
||||
case uint64:
|
||||
return in.Uint64() != 0
|
||||
case float64:
|
||||
return in.Float64() != 0
|
||||
case time.Duration:
|
||||
return in.Duration() != 0
|
||||
case time.Time:
|
||||
return !in.Time().IsZero()
|
||||
case string:
|
||||
return len(in.String()) > 0
|
||||
default:
|
||||
return in.Any() != nil
|
||||
}
|
||||
}
|
||||
128
validator/not_blank_test.go
Normal file
128
validator/not_blank_test.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package validator_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitoa.ru/go-4devs/config"
|
||||
"gitoa.ru/go-4devs/config/definition/option"
|
||||
"gitoa.ru/go-4devs/config/validator"
|
||||
"gitoa.ru/go-4devs/config/value"
|
||||
)
|
||||
|
||||
func TestNotBlank(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for name, ca := range casesNotBlank() {
|
||||
valid := validator.NotBlank
|
||||
|
||||
if err := valid(ca.vars, ca.value); err != nil {
|
||||
t.Errorf("case: %s, expected error <nil>, got: %s", name, err)
|
||||
}
|
||||
|
||||
if ca.empty == nil {
|
||||
ca.empty = value.EmptyValue()
|
||||
}
|
||||
|
||||
emptErr := valid(ca.vars, ca.empty)
|
||||
if emptErr == nil || !errors.Is(emptErr, validator.ErrNotBlank) {
|
||||
t.Errorf("case empty: %s, expect: %s, got:%v", name, validator.ErrNotBlank, emptErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type cases struct {
|
||||
vars config.Option
|
||||
value config.Value
|
||||
empty config.Value
|
||||
}
|
||||
|
||||
func casesNotBlank() map[string]cases {
|
||||
return map[string]cases{
|
||||
"any": {
|
||||
vars: option.New("any", "any", nil),
|
||||
value: value.New(float32(1)),
|
||||
},
|
||||
"array int": {
|
||||
vars: option.Int("int", "array int", option.Slice),
|
||||
value: value.New([]int{1}),
|
||||
empty: value.New([]int{}),
|
||||
},
|
||||
"array int64": {
|
||||
vars: option.Int64("int64", "array int64", option.Slice),
|
||||
value: value.New([]int64{1}),
|
||||
empty: value.New([]int64{}),
|
||||
},
|
||||
"array uint": {
|
||||
vars: option.Uint("uint", "array uint", option.Slice),
|
||||
value: value.New([]uint{1}),
|
||||
empty: value.New([]uint{}),
|
||||
},
|
||||
"array uint64": {
|
||||
vars: option.Uint64("uint64", "array uint64", option.Slice),
|
||||
value: value.New([]uint64{1}),
|
||||
empty: value.New([]uint64{}),
|
||||
},
|
||||
"array float64": {
|
||||
vars: option.Float64("float64", "array float64", option.Slice),
|
||||
value: value.New([]float64{0.2}),
|
||||
empty: value.New([]float64{}),
|
||||
},
|
||||
"array bool": {
|
||||
vars: option.Bool("bool", "array bool", option.Slice),
|
||||
value: value.New([]bool{true, false}),
|
||||
empty: value.New([]bool{}),
|
||||
},
|
||||
"array duration": {
|
||||
vars: option.Duration("duration", "array duration", option.Slice),
|
||||
value: value.New([]time.Duration{time.Second}),
|
||||
empty: value.New([]time.Duration{}),
|
||||
},
|
||||
"array time": {
|
||||
vars: option.Time("time", "array time", option.Slice),
|
||||
value: value.New([]time.Time{time.Now()}),
|
||||
empty: value.New([]time.Time{}),
|
||||
},
|
||||
"array string": {
|
||||
vars: option.String("string", "array string", option.Slice),
|
||||
value: value.New([]string{"value"}),
|
||||
empty: value.New([]string{}),
|
||||
},
|
||||
"int": {
|
||||
vars: option.Int("int", "int"),
|
||||
value: value.New(int(1)),
|
||||
},
|
||||
"int64": {
|
||||
vars: option.Int64("int64", "int64"),
|
||||
value: value.New(int64(2)),
|
||||
},
|
||||
"uint": {
|
||||
vars: option.Uint("uint", "uint"),
|
||||
value: value.New(uint(1)),
|
||||
empty: value.New([]uint{1}),
|
||||
},
|
||||
"uint64": {
|
||||
vars: option.Uint64("uint64", "uint64"),
|
||||
value: value.New(uint64(10)),
|
||||
},
|
||||
"float64": {
|
||||
vars: option.Float64("float64", "float64"),
|
||||
value: value.New(float64(.00001)),
|
||||
},
|
||||
"duration": {
|
||||
vars: option.Duration("duration", "duration"),
|
||||
value: value.New(time.Minute),
|
||||
empty: value.New("same string"),
|
||||
},
|
||||
"time": {
|
||||
vars: option.Time("time", "time"),
|
||||
value: value.New(time.Now()),
|
||||
},
|
||||
"string": {
|
||||
vars: option.String("string", "string"),
|
||||
value: value.New("string"),
|
||||
empty: value.New(""),
|
||||
},
|
||||
}
|
||||
}
|
||||
49
validator/valid.go
Normal file
49
validator/valid.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitoa.ru/go-4devs/config"
|
||||
"gitoa.ru/go-4devs/config/param"
|
||||
)
|
||||
|
||||
func Chain(v ...Validator) Validator {
|
||||
return func(vr param.Params, in config.Value) error {
|
||||
for _, valid := range v {
|
||||
err := valid(vr, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const paramValid = "param.valid"
|
||||
|
||||
type Validator func(param.Params, config.Value) error
|
||||
|
||||
func Valid(in ...Validator) param.Option {
|
||||
return func(v param.Params) param.Params {
|
||||
return param.With(v, paramValid, in)
|
||||
}
|
||||
}
|
||||
|
||||
func Validate(key []string, fn param.Params, in config.Value) error {
|
||||
params, ok := fn.Param(paramValid)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
valids, _ := params.([]Validator)
|
||||
|
||||
for _, valid := range valids {
|
||||
err := valid(fn, in)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s:%w", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
33
validator/valid_test.go
Normal file
33
validator/valid_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package validator_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"gitoa.ru/go-4devs/config/definition/option"
|
||||
"gitoa.ru/go-4devs/config/validator"
|
||||
"gitoa.ru/go-4devs/config/value"
|
||||
)
|
||||
|
||||
func TestChain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
vars := option.String("test", "test")
|
||||
validValue := value.New("one")
|
||||
invalidValue := value.New([]string{"one"})
|
||||
|
||||
valid := validator.Chain(
|
||||
validator.NotBlank,
|
||||
validator.Enum("one", "two"),
|
||||
)
|
||||
|
||||
err := valid(vars, validValue)
|
||||
if err != nil {
|
||||
t.Errorf("expected valid value, got: %s", err)
|
||||
}
|
||||
|
||||
ierr := valid(vars, invalidValue)
|
||||
if !errors.Is(ierr, validator.ErrNotBlank) {
|
||||
t.Errorf("expected not blank, got:%s", ierr)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user