Skip to content

Commit

Permalink
Merge pull request #34 from twharmon/lazy-registration
Browse files Browse the repository at this point in the history
Lazy registration
  • Loading branch information
twharmon authored Oct 14, 2021
2 parents 137f519 + 4a020b1 commit 12bc229
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 340 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ func main() {
}
return fmt.Sprintf("%s must contain more exclamation marks", field)
})

v.Register(Post{}) // Register all structs at load time

p := Post{
ID: 5,
Expand All @@ -80,9 +78,9 @@ func main() {
## Benchmarks

```
BenchmarkValidatorStringReqInvalid 192 ns/op 48 B/op 3 allocs/op
BenchmarkValidatorStringReqValid 98.5 ns/op 16 B/op 1 allocs/op
BenchmarkValidatorsVariety 1114 ns/op 281 B/op 13 allocs/op
BenchmarkValidatorStringReqInvalid 267.3 ns/op 48 B/op 3 allocs/op
BenchmarkValidatorStringReqValid 92.35 ns/op 16 B/op 1 allocs/op
BenchmarkValidatorsVariety 1484 ns/op 297 B/op 15 allocs/op
```

## Contribute
Expand Down
5 changes: 2 additions & 3 deletions govalid.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import (
// a type that is not a struct is made.
var ErrNotStruct = errors.New("only structs can be validated")

// ErrNotRegistered is encountered when an attempt is made to
// validate a type that has not yet been registered.
// Deprecated: ErrNotRegistered is deprecated.
var ErrNotRegistered = errors.New("structs must be registered before validating")

// New .
func New() *Validator {
v := new(Validator)
v.modelStore = make(map[string]*model)
v.store = make(map[string]*model)
v.stringRules = make(map[string]func(string, string) string)
v.int64Rules = make(map[string]func(string, int64) string)
v.float64Rules = make(map[string]func(string, float64) string)
Expand Down
20 changes: 0 additions & 20 deletions govalid_test.go

This file was deleted.

6 changes: 0 additions & 6 deletions testing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ func contains(t *testing.T, haystack string, needles ...string) {
}
}

func notEqual(t *testing.T, a interface{}, b interface{}) {
if a == b {
t.Fatalf("expected %v to not equal %v", a, b)
}
}

func check(t *testing.T, err error) {
if err != nil {
t.Fatal(err)
Expand Down
57 changes: 37 additions & 20 deletions validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@ package govalid

import (
"errors"
"fmt"
"reflect"
"strings"
"sync"
)

// Validator .
type Validator struct {
modelStore map[string]*model
store map[string]*model
mu sync.RWMutex
stringRules map[string]func(string, string) string
int64Rules map[string]func(string, int64) string
float64Rules map[string]func(string, float64) string
}

// Register is required for all structs that you wish
// to validate. It is intended to be ran at load time
// and caches information about the structs to reduce
// run time allocations.
// Deprecated: Register is deprecated. Structs are registered as
// needed.
func (v *Validator) Register(structs ...interface{}) error {
for _, s := range structs {
if err := v.register(s); err != nil {
Expand All @@ -35,9 +34,16 @@ func (v *Validator) AddCustom(s interface{}, f ...func(interface{}) string) erro
t = t.Elem()
}
n := t.Name()
m := v.modelStore[n]
v.mu.RLock()
m := v.store[n]
v.mu.RUnlock()
if m == nil {
return ErrNotRegistered
if err := v.register(s); err != nil {
return err
}
v.mu.RLock()
m = v.store[n]
v.mu.RUnlock()
}
m.custom = append(m.custom, f...)
return nil
Expand All @@ -61,8 +67,7 @@ func (v *Validator) AddCustomFloat64Rule(name string, validatorFunc func(string,
// Violation checks the struct s against all constraints and custom
// validation functions, if any. It returns an violation if the
// struct fails validation. If the type being validated is not a
// struct, ErrNotStruct will be returned. If the type being validated
// has not yet been registered, ErrNotRegistered is returned.
// struct, ErrNotStruct will be returned.
func (v *Validator) Violation(s interface{}) (string, error) {
t := reflect.TypeOf(s)
for t.Kind() == reflect.Ptr {
Expand All @@ -71,18 +76,24 @@ func (v *Validator) Violation(s interface{}) (string, error) {
if t.Kind() != reflect.Struct {
return "", ErrNotStruct
}
m := v.modelStore[t.Name()]
v.mu.RLock()
m := v.store[t.Name()]
v.mu.RUnlock()
if m == nil {
return "", ErrNotRegistered
if err := v.register(s); err != nil {
return "", err
}
v.mu.RLock()
m = v.store[t.Name()]
v.mu.RUnlock()
}
return m.violation(s), nil
}

// Violations checks the struct s against all constraints and custom
// validation functions, if any. It returns a slice of violations if
// the struct fails validation. If the type being validated is not a
// struct, ErrNotStruct will be returned. If the type being validated
// has not yet been registered, ErrNotRegistered is returned.
// struct, ErrNotStruct will be returned.
func (v *Validator) Violations(s interface{}) ([]string, error) {
t := reflect.TypeOf(s)
for t.Kind() == reflect.Ptr {
Expand All @@ -91,9 +102,16 @@ func (v *Validator) Violations(s interface{}) ([]string, error) {
if t.Kind() != reflect.Struct {
return nil, ErrNotStruct
}
m := v.modelStore[t.Name()]
v.mu.RLock()
m := v.store[t.Name()]
v.mu.RUnlock()
if m == nil {
return nil, ErrNotRegistered
if err := v.register(s); err != nil {
return nil, err
}
v.mu.RLock()
m = v.store[t.Name()]
v.mu.RUnlock()
}
return m.violations(s), nil
}
Expand Down Expand Up @@ -152,9 +170,8 @@ func (v *Validator) register(s interface{}) error {
}

func (v *Validator) addModelToRegistry(m *model, name string) error {
if v.modelStore[name] != nil {
return fmt.Errorf("%s is already registered", name)
}
v.modelStore[name] = m
v.mu.Lock()
v.store[name] = m
v.mu.Unlock()
return nil
}
Loading

0 comments on commit 12bc229

Please sign in to comment.