-
Notifications
You must be signed in to change notification settings - Fork 1
/
rule.go
82 lines (67 loc) · 2.09 KB
/
rule.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package vdutil
import (
"context"
"errors"
"github.com/jxskiss/gopkg/v2/easy/ezmap"
)
// Rule represents a validating rule.
type Rule interface {
Validate(ctx context.Context, result *Result) (any, error)
}
// RuleFunc implements the interface Rule.
type RuleFunc func(ctx context.Context, result *Result) (any, error)
func (f RuleFunc) Validate(ctx context.Context, result *Result) (any, error) {
return f(ctx, result)
}
// Result can be used to pass data from validating rules to the caller.
type Result struct {
// Data allows validating rules to pass data to the caller.
Data ezmap.Map
// Validating rules can optionally add detail information to ErrDetails
// to inform the caller.
ErrDetails []any
// IsValidationError tells whether the error returned by Validate
// is a ValidationError or not.
IsValidationError bool
}
// ValidationError indicates that the data is invalid.
// Name and Err tells which data and underlying error returned by a Rule.
// Note, a Rule should not return this error on programming error
// or internal system error.
type ValidationError struct {
Name string
Err error
}
func (e *ValidationError) Error() string { return e.Name + ": " + e.Err.Error() }
func (e *ValidationError) Unwrap() error { return e.Err }
// Validate runs validating rules, it returns a Result and the first error
// returned from the rules. If a rule returns an error, it returns
// and the remaining rules are not executed.
func Validate(ctx context.Context, rules ...Rule) (result *Result, err error) {
for _, r := range rules {
if rr, ok := r.(*useResult); ok {
result = (*Result)(rr)
}
}
if result == nil {
result = &Result{}
}
for _, rule := range rules {
_, err = rule.Validate(ctx, result)
if err != nil {
break
}
}
if err != nil {
result.IsValidationError = errors.As(err, new(*ValidationError))
}
return result, err
}
// UseResult tells validating rules to use the given result.
func UseResult(result *Result) Rule {
return (*useResult)(result)
}
type useResult Result
func (*useResult) Validate(_ context.Context, _ *Result) (any, error) {
return nil, nil
}