Skip to content

Commit

Permalink
feat: add rules
Browse files Browse the repository at this point in the history
  • Loading branch information
franklinkim committed Mar 13, 2023
1 parent dd351b8 commit 9e1e071
Show file tree
Hide file tree
Showing 52 changed files with 1,230 additions and 611 deletions.
3 changes: 0 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ import (
"github.com/go-playground/validator/v10"
)

// EmailRegexWeak as https://davidcel.is/posts/stop-validating-email-addresses-with-regex/
var EmailRegexWeak = `.+@.+\..+`

var Validator = validator.New()

func init() {
Expand Down
15 changes: 4 additions & 11 deletions config/delimter.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
package config

const (
DefaultFendDelimiter = ";"
DefaultFendNameDelimiter = ":"
DefaultRuleDelimiter = ":"
DefaultRuleMetaDelimiter = "="
)

var (
FendDelimiter = DefaultFendDelimiter
FendNameDelimiter = DefaultFendNameDelimiter
RuleDelimiter = DefaultRuleDelimiter
RuleMetaDelimiter = DefaultRuleMetaDelimiter
DelimiterFend = ";"
DelimiterFendName = ":"
DelimiterRule = ":"
DelimiterRuleMeta = "="
)
15 changes: 15 additions & 0 deletions config/regex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package config

import (
"regexp"
)

var (
RegexEmailWeak = regexp.MustCompile(`.+@.+\..+`) // https://davidcel.is/posts/stop-validating-email-addresses-with-regex/
RegexHostname = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`) // https://tools.ietf.org/html/rfc952
RegexAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`)
RegexNumeric = regexp.MustCompile(`^[0-9]+$`)
RegexAlpha = regexp.MustCompile(`^[a-zA-Z]+$`)
RegexUUID = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)
RegexMD5 = regexp.MustCompile(`^[0-9a-f]{32}$`)
)
12 changes: 11 additions & 1 deletion error.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"strings"

"github.com/foomo/fender/config"
"github.com/foomo/fender/fend"
"github.com/foomo/fender/rule"
"go.uber.org/multierr"
)

Expand All @@ -18,13 +20,21 @@ func NewError(cause error) *Error {
}
}

func NewFendError(name string, cause error) *Error {
return NewError(fend.NewError(name, cause))
}

func NewFendRuleError(name string, ruleName rule.Name, ruleMeta ...string) *Error {
return NewFendError(name, rule.NewError(ruleName, ruleMeta...))
}

func (e *Error) Error() string {
errs := multierr.Errors(e.cause)
causes := make([]string, len(errs))
for i, cause := range errs {
causes[i] = cause.Error()
}
return strings.Join(causes, config.FendDelimiter)
return strings.Join(causes, config.DelimiterFend)
}

func (e *Error) First() error {
Expand Down
2 changes: 1 addition & 1 deletion fend/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (e *Error) Error() string {
for i, cause := range errs {
causes[i] = cause.Error()
}
return e.name + config.FendNameDelimiter + strings.Join(causes, config.RuleDelimiter)
return e.name + config.DelimiterFendName + strings.Join(causes, config.DelimiterRule)
}

func (e *Error) Errors() []error {
Expand Down
48 changes: 48 additions & 0 deletions fend/fend.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,54 @@ package fend

import (
"context"
"errors"

"github.com/foomo/fender/rule"
"go.uber.org/multierr"
)

type Fend func(ctx context.Context, mode Mode) error

func fend[T any](ctx context.Context, mode Mode, meta string, value T, rules ...rule.Rule[T]) error {
var causes error
for _, r := range rules {
err := r(ctx, value)
if errors.Is(err, rule.ErrBreak) {
break
} else if e, ok := err.(*rule.Error); ok { //nolint:errorlint
causes = multierr.Append(causes, e)
// break if we only want the first error
if mode == ModeFirst {
break
}
} else if err != nil {
return err
}
}
if causes != nil {
return NewError(meta, causes)
}
return nil
}

func fendFn(ctx context.Context, mode Mode, meta string, rules ...rule.Fn) error {
var causes error
for _, r := range rules {
err := r(ctx)
if errors.Is(err, rule.ErrBreak) {
break
} else if e, ok := err.(*rule.Error); ok { //nolint:errorlint
causes = multierr.Append(causes, e)
// break if we only want the first error
if mode == ModeFirst {
break
}
} else if err != nil {
return err
}
}
if causes != nil {
return NewError(meta, causes)
}
return nil
}
8 changes: 8 additions & 0 deletions fend/fends.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
package fend

type Fends []Fend

func (f Fends) Append(v ...Fend) Fends {
return append(f, v...)
}

func (f Fends) Prepend(v ...Fend) Fends {
return append(v, f...)
}
34 changes: 6 additions & 28 deletions fend/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,16 @@ import (
"context"

"github.com/foomo/fender/rule"
"go.uber.org/multierr"
)

type FieldError struct {
Name string
Errors []error
}

func (e *FieldError) Error() string {
return e.Name
func Field[T any](name string, value T, rules ...rule.Rule[T]) Fend {
return func(ctx context.Context, mode Mode) error {
return fend(ctx, mode, name, value, rules...)
}
}

// Field
// should be func FendField[T any](name string, value T, rules ...rule.Rule[T]) validation.Validator {}
func Field[T any](name string, value T, rules ...func(ctx context.Context, v T) error) Fend {
func FieldFn(name string, rules ...rule.Fn) Fend {
return func(ctx context.Context, mode Mode) error {
var causes error
for _, r := range rules {
err := r(ctx, value)
if e, ok := err.(*rule.Error); ok { //nolint:errorlint
causes = multierr.Append(causes, e)
// break if we only want the first error
if mode == ModeFirst {
break
}
} else if err != nil {
return err
}
}
if causes != nil {
return NewError(name, causes)
}
return nil
return fendFn(ctx, mode, name, rules...)
}
}
13 changes: 13 additions & 0 deletions fend/fn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package fend

import (
"context"

"github.com/foomo/fender/rule"
)

func Fn(rules ...rule.Fn) Fend {
return func(ctx context.Context, mode Mode) error {
return fendFn(ctx, mode, "", rules...)
}
}
27 changes: 1 addition & 26 deletions fend/var.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,10 @@ import (
"context"

"github.com/foomo/fender/rule"
"go.uber.org/multierr"
)

type VarError struct {
Errors []error
}

func (e *VarError) Error() string {
return ""
}

func Var[T any](value T, rules ...rule.Rule[T]) Fend {
return func(ctx context.Context, mode Mode) error {
var cause error
for _, r := range rules {
err := r(ctx, value)
if e, ok := err.(*rule.Error); ok { //nolint:errorlint
cause = multierr.Append(cause, e)
// break if we only want the first error
if mode == ModeFirst {
break
}
} else if err != nil {
return err
}
}
if cause != nil {
return NewError("", cause)
}
return nil
return fend(ctx, mode, "", value, rules...)
}
}
44 changes: 22 additions & 22 deletions fender_benchmarks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@ func BenchmarkAll(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = fender.All(
context.TODO(),
fend.Field("int", u.Int, rule.RequiredInt, rule.MinInt(1), rule.MaxInt(5)),
fend.Field("int8", u.Int8, rule.RequiredInt8, rule.MinInt8(1), rule.MaxInt8(5)),
fend.Field("int32", u.Int32, rule.RequiredInt32, rule.MinInt32(1), rule.MaxInt32(5)),
fend.Field("int64", u.Int64, rule.RequiredInt64, rule.MinInt64(1), rule.MaxInt64(5)),
fend.Field("uint", u.UInt, rule.RequiredUInt, rule.MinUInt(1), rule.MaxUInt(5)),
fend.Field("uint8", u.UInt8, rule.RequiredUInt8, rule.MinUInt8(1), rule.MaxUInt8(5)),
fend.Field("uint32", u.UInt32, rule.RequiredUInt32, rule.MinUInt32(1), rule.MaxUInt32(5)),
fend.Field("uint64", u.UInt64, rule.RequiredUInt64, rule.MinUInt64(1), rule.MaxUInt64(5)),
fend.Field("float32", u.Float32, rule.RequiredFloat32, rule.MinFloat32(1), rule.MaxFloat32(5)),
fend.Field("float64", u.Float64, rule.RequiredFloat64, rule.MinFloat64(1), rule.MaxFloat64(5)),
fend.Field("int", u.Int, rule.IntRequired, rule.IntMin(1), rule.IntMax(5)),
fend.Field("int8", u.Int8, rule.Int8Required, rule.Int8Min(1), rule.Int8Max(5)),
fend.Field("int32", u.Int32, rule.Int32Required, rule.Int32Min(1), rule.Int32Max(5)),
fend.Field("int64", u.Int64, rule.Int64Required, rule.Int64Min(1), rule.Int64Max(5)),
fend.Field("uint", u.UInt, rule.UIntRequired, rule.UIntMin(1), rule.UIntMax(5)),
fend.Field("uint8", u.UInt8, rule.UInt8Required, rule.UInt8Min(1), rule.UInt8Max(5)),
fend.Field("uint32", u.UInt32, rule.UInt32Required, rule.UInt32Min(1), rule.UInt32Max(5)),
fend.Field("uint64", u.UInt64, rule.UInt64Required, rule.UInt64Min(1), rule.UInt64Max(5)),
fend.Field("float32", u.Float32, rule.Float32Required, rule.Float32Min(1), rule.Float32Max(5)),
fend.Field("float64", u.Float64, rule.Float64Required, rule.Float64Min(1), rule.Float64Max(5)),
fend.Field("bool", u.Bool, rule.Bool(true)),
fend.Field("string", u.String, rule.RequiredString),
fend.Field("string", u.String, rule.StringRequired),
)
}
})
Expand Down Expand Up @@ -77,18 +77,18 @@ func BenchmarkAll(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = fender.All(
context.TODO(),
fend.Field("int", u.Int, rule.RequiredInt, rule.MinInt(1), rule.MaxInt(5)),
fend.Field("int8", u.Int8, rule.RequiredInt8, rule.MinInt8(1), rule.MaxInt8(5)),
fend.Field("int32", u.Int32, rule.RequiredInt32, rule.MinInt32(1), rule.MaxInt32(5)),
fend.Field("int64", u.Int64, rule.RequiredInt64, rule.MinInt64(1), rule.MaxInt64(5)),
fend.Field("uint", u.UInt, rule.RequiredUInt, rule.MinUInt(1), rule.MaxUInt(5)),
fend.Field("uint8", u.UInt8, rule.RequiredUInt8, rule.MinUInt8(1), rule.MaxUInt8(5)),
fend.Field("uint32", u.UInt32, rule.RequiredUInt32, rule.MinUInt32(1), rule.MaxUInt32(5)),
fend.Field("uint64", u.UInt64, rule.RequiredUInt64, rule.MinUInt64(1), rule.MaxUInt64(5)),
fend.Field("float32", u.Float32, rule.RequiredFloat32, rule.MinFloat32(1), rule.MaxFloat32(5)),
fend.Field("float64", u.Float64, rule.RequiredFloat64, rule.MinFloat64(1), rule.MaxFloat64(5)),
fend.Field("int", u.Int, rule.IntRequired, rule.IntMin(1), rule.IntMax(5)),
fend.Field("int8", u.Int8, rule.Int8Required, rule.Int8Min(1), rule.Int8Max(5)),
fend.Field("int32", u.Int32, rule.Int32Required, rule.Int32Min(1), rule.Int32Max(5)),
fend.Field("int64", u.Int64, rule.Int64Required, rule.Int64Min(1), rule.Int64Max(5)),
fend.Field("uint", u.UInt, rule.UIntRequired, rule.UIntMin(1), rule.UIntMax(5)),
fend.Field("uint8", u.UInt8, rule.UInt8Required, rule.UInt8Min(1), rule.UInt8Max(5)),
fend.Field("uint32", u.UInt32, rule.UInt32Required, rule.UInt32Min(1), rule.UInt32Max(5)),
fend.Field("uint64", u.UInt64, rule.UInt64Required, rule.UInt64Min(1), rule.UInt64Max(5)),
fend.Field("float32", u.Float32, rule.Float32Required, rule.Float32Min(1), rule.Float32Max(5)),
fend.Field("float64", u.Float64, rule.Float64Required, rule.Float64Min(1), rule.Float64Max(5)),
fend.Field("bool", u.Bool, rule.Bool(true)),
fend.Field("string", u.String, rule.RequiredString),
fend.Field("string", u.String, rule.StringRequired),
)
}
})
Expand Down
16 changes: 8 additions & 8 deletions fender_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
func ExampleAll() {
err := fender.All(
context.Background(),
fend.Field("one", "", rule.RequiredString, rule.MinString(10)),
fend.Field("two", "", rule.RequiredString, rule.MinString(10)),
fend.Field("one", "", rule.StringRequired, rule.StringMin(10)),
fend.Field("two", "", rule.StringRequired, rule.StringMin(10)),
)
// check for fender error
if fendErr := fender.AsError(err); fendErr != nil {
Expand All @@ -27,8 +27,8 @@ func ExampleAll() {
func ExampleFirst() {
err := fender.First(
context.Background(),
fend.Field("one", "", rule.RequiredString, rule.MinString(10)),
fend.Field("two", "", rule.RequiredString, rule.MinString(10)),
fend.Field("one", "", rule.StringRequired, rule.StringMin(10)),
fend.Field("two", "", rule.StringRequired, rule.StringMin(10)),
)

// check for fender error
Expand All @@ -43,8 +43,8 @@ func ExampleFirst() {
func ExampleAllFirst() {
err := fender.AllFirst(
context.Background(),
fend.Field("one", "", rule.RequiredString, rule.MinString(10)),
fend.Field("two", "", rule.RequiredString, rule.MinString(10)),
fend.Field("one", "", rule.StringRequired, rule.StringMin(10)),
fend.Field("two", "", rule.StringRequired, rule.StringMin(10)),
)

// check for fender error
Expand All @@ -59,8 +59,8 @@ func ExampleAllFirst() {
func ExampleErrors() { //nolint:govet
err := fender.All(
context.Background(),
fend.Field("one", "", rule.RequiredString, rule.MinString(10)),
fend.Field("two", "", rule.RequiredString, rule.MinString(10)),
fend.Field("one", "", rule.StringRequired, rule.StringMin(10)),
fend.Field("two", "", rule.StringRequired, rule.StringMin(10)),
)

// cast fender error
Expand Down

0 comments on commit 9e1e071

Please sign in to comment.