Skip to content

Commit

Permalink
docs: add examples
Browse files Browse the repository at this point in the history
  • Loading branch information
franklinkim committed Mar 8, 2023
1 parent 21a020e commit e84c11b
Show file tree
Hide file tree
Showing 7 changed files with 320 additions and 317 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ test:
.PHONY: bench
## Run benchmarks
bench:
go test -bench | prettybench
go test -bench=. | prettybench

.PHONY: lint
## Run linter
Expand Down
220 changes: 98 additions & 122 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,146 +6,126 @@ Fender provides a unified way to validate data and make the errors passable to t

## Usage

Fender provides different ways to validate your data.

### struct validation using go-playground validator
### All

```go
package main

import (
"fmt"

"github.com/foomo/fender"
)

type Test struct {
Int int `validate:"min=1"`
Int8 int8 `validate:"min=1"`
Int32 int32 `validate:"min=1"`
Int64 int64 `validate:"min=1"`
UInt uint `validate:"min=1"`
UInt8 uint8 `validate:"min=1"`
UInt32 uint32 `validate:"min=1"`
UInt64 uint64 `validate:"min=1"`
Float32 float32 `validate:"min=1.5"`
Float64 float64 `validate:"min=1.5"`
Bool bool `validate:"required"`
String string `validate:"required"`
}

func main() {
u := Test{}
fendErr, err := fender.Struct(u)
if err != nil {
panic("internal error: " + err.Error())
func ExampleAll() {
err := fender.All(
context.Background(),
fend.Field("one", "", rule.RequiredString, rule.MinString(10)),
fend.Field("two", "", rule.RequiredString, rule.MinString(10)),
)
// check for fender error
if fendErr := fender.AsError(err); fendErr != nil {
fmt.Println(err)
} else if err != nil {
panic(err)
}
fmt.Println(fendErr) // bool:required;string:required;uInt:min=1;uInt8:min=1;uInt32:min=1;uInt64:min=1;float32:min=1.5;int:min=1;int8:min=1;int32:min=1;int64:min=1;float64:min=1.5
// Output: one:required:min=10;two:required:min=10
}
```

### validate by code
### AllFirst

```go
package main

import (
"fmt"

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

type Test struct {
Int int
Int8 int8
Int32 int32
Int64 int64
UInt uint
UInt8 uint8
UInt32 uint32
UInt64 uint64
Float32 float32
Float64 float64
Bool bool
String string
}

func main() {
u := Test{}
fendErr, err := fender.All(
fender.Field("int", fend.Int(u.Int, rule.MinInt(1))),
fender.Field("int8", fend.Int8(u.Int8, rule.MinInt8(1))),
fender.Field("int32", fend.Int32(u.Int32, rule.MinInt32(1))),
fender.Field("int64", fend.Int64(u.Int64, rule.MinInt64(1))),
fender.Field("uint", fend.UInt(u.UInt, rule.MinUInt(1))),
fender.Field("uint8", fend.UInt8(u.UInt8, rule.MinUInt8(1))),
fender.Field("uint32", fend.UInt32(u.UInt32, rule.MinUInt32(1))),
fender.Field("uint64", fend.UInt64(u.UInt64, rule.MinUInt64(1))),
fender.Field("float32", fend.Float32(u.Float32, rule.MinFloat32(1.5))),
fender.Field("float64", fend.Float64(u.Float64, rule.MinFloat64(1.5))),
fender.Field("bool", fend.Bool(u.Bool, rule.Bool(true))),
fender.Field("string", fend.String(u.String, rule.RequiredString)),
func ExampleAllFirst() {
err := fender.AllFirst(
context.Background(),
fend.Field("one", "", rule.RequiredString, rule.MinString(10)),
fend.Field("two", "", rule.RequiredString, rule.MinString(10)),
)
if err != nil {
panic("internal error: " + err.Error())

// check for fender error
if fendErr := fender.AsError(err); fendErr != nil {
fmt.Println(err)
} else if err != nil {
panic(err)
}
fmt.Println(fendErr) // uint:min=1;uint64:min=1;float32:min=1.5;int:min=1;int8:min=1;uint8:min=1;uint32:min=1;float64:min=1.5;bool:bool=true;string:required;int32:min=1;int64:min=1
// Output: one:required:min=10
}
```

### validate by code and stop on first error
### First

```go
package main

import (
"fmt"

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

type Test struct {
Int int
Int8 int8
Int32 int32
Int64 int64
UInt uint
UInt8 uint8
UInt32 uint32
UInt64 uint64
Float32 float32
Float64 float64
Bool bool
String string
func ExampleFirst() {
err := fender.First(
context.Background(),
fend.Field("one", "", rule.RequiredString, rule.MinString(10)),
fend.Field("two", "", rule.RequiredString, rule.MinString(10)),
)

// check for fender error
if fendErr := fender.AsError(err); fendErr != nil {
fmt.Println(err)
} else if err != nil {
panic(err)
}
// Output: one:required
}
```

### Handle errors

func main() {
u := Test{}
fendErr, err := fender.First(
fender.Field("int", fend.Int(u.Int, rule.MinInt(1))),
fender.Field("int8", fend.Int8(u.Int8, rule.MinInt8(1))),
fender.Field("int32", fend.Int32(u.Int32, rule.MinInt32(1))),
fender.Field("int64", fend.Int64(u.Int64, rule.MinInt64(1))),
fender.Field("uint", fend.UInt(u.UInt, rule.MinUInt(1))),
fender.Field("uint8", fend.UInt8(u.UInt8, rule.MinUInt8(1))),
fender.Field("uint32", fend.UInt32(u.UInt32, rule.MinUInt32(1))),
fender.Field("uint64", fend.UInt64(u.UInt64, rule.MinUInt64(1))),
fender.Field("float32", fend.Float32(u.Float32, rule.MinFloat32(1.5))),
fender.Field("float64", fend.Float64(u.Float64, rule.MinFloat64(1.5))),
fender.Field("bool", fend.Bool(u.Bool, rule.Bool(true))),
fender.Field("string", fend.String(u.String, rule.RequiredString)),
```go
func ExampleErrors() {
err := fender.All(
context.Background(),
fend.Field("one", "", rule.RequiredString, rule.MinString(10)),
fend.Field("two", "", rule.RequiredString, rule.MinString(10)),
)
if err != nil {
panic("internal error: " + err.Error())

// cast fender error
if fenderErr := fender.AsError(err); fenderErr != nil {

// iterate fend errors
for _, err := range fenderErr.Errors() {

// cast fend error
if fendErr := fend.AsError(err); fendErr != nil {

fmt.Println(fendErr.Name())

// iterate rule errors
for _, err := range fendErr.Errors() {

// cast rule error
if ruleErr := rule.AsError(err); ruleErr != nil {
fmt.Println(ruleErr.Error())
}
}
}
}
} else if err != nil {
panic(err)
}
fmt.Println(fendErr) // int:min=1
// Output:
// one
// required
// min=10
// two
// required
// min=10
}
```

## Benchmarks

```text
go test -bench=. | prettybench
goos: darwin
goarch: arm64
pkg: github.com/foomo/fender
PASS
benchmark iter time/iter
--------- ---- ---------
BenchmarkAll/invalid_reused/fender-10 282154 3771.00 ns/op
BenchmarkAll/invalid_reused/playground-10 678679 1741.00 ns/op
BenchmarkAll/success_new/fender-10 1000000 1026.00 ns/op
BenchmarkAll/success_new/playground-10 1308327 937.60 ns/op
ok github.com/foomo/fender 5.619s
```

## References & alternatives

- [go-playground/validator](https://github.com/go-playground/validator)
Expand All @@ -158,7 +138,3 @@ Make a pull request...
## License

Distributed under MIT License, please see license file within the code for more details.

## Notes

We'll take another refactoring round as soon as go supports generics
9 changes: 9 additions & 0 deletions fend/error.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fend

import (
"errors"
"strings"

"github.com/foomo/fender/config"
Expand Down Expand Up @@ -39,3 +40,11 @@ func (e *Error) Errors() []error {
func (e *Error) Unwrap() error {
return e.cause
}

func AsError(err error) *Error {
var fendErr *Error
if errors.As(err, &fendErr) {
return fendErr
}
return nil
}
8 changes: 4 additions & 4 deletions fend/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ func (e *FieldError) Error() string {
// 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 {
return func(ctx context.Context, mode Mode) error {
var cause error
var causes error
for _, r := range rules {
err := r(ctx, value)
if e, ok := err.(*rule.Error); ok { //nolint:errorlint
cause = multierr.Append(cause, e)
causes = multierr.Append(causes, e)
// break if we only want the first error
if mode == ModeFirst {
break
Expand All @@ -33,8 +33,8 @@ func Field[T any](name string, value T, rules ...func(ctx context.Context, v T)
return err
}
}
if cause != nil {
return NewError(name, cause)
if causes != nil {
return NewError(name, causes)
}
return nil
}
Expand Down

0 comments on commit e84c11b

Please sign in to comment.