Skip to content

Commit

Permalink
Merge pull request #21 from foomo/feature/generics
Browse files Browse the repository at this point in the history
Use generics
  • Loading branch information
franklinkim committed Apr 7, 2023
2 parents ca0ae8a + b47f5d2 commit cdae976
Show file tree
Hide file tree
Showing 81 changed files with 1,518 additions and 1,715 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/pull-requests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ jobs:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/setup-go@v3
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
cache: true
check-latest: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/releases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v3
with:
fetch-depth: 0
- run: git fetch --force --tags
Expand Down
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ check: test lint
test:
gotestsum --format dots-v2 ./...

.PHONY: bench
## Run benchmarks
bench:
go test -run ^$$ -bench . | prettybench

.PHONY: lint
## Run linter
lint:
Expand Down
222 changes: 99 additions & 123 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,150 +2,130 @@

> a piece of rope or a tyre that protects the side of a boat from knocks
Fender provides a unified way to validate data and make the errors passable to the frontend e.g. through gotsrpc.
Fender provides a unified way to validate data and on the backend & frontend side.

## 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
22 changes: 0 additions & 22 deletions all.go

This file was deleted.

71 changes: 0 additions & 71 deletions all_test.go

This file was deleted.

0 comments on commit cdae976

Please sign in to comment.