-
Notifications
You must be signed in to change notification settings - Fork 3
/
types.go
162 lines (139 loc) · 4.27 KB
/
types.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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package constraint
import (
"fmt"
"reflect"
"github.com/jt0/gomer/flect"
"github.com/jt0/gomer/gomerr"
"github.com/jt0/gomer/structs"
)
// UseBracketsForContainedTargets specifies whether the generated target value is in 'json' or 'Gomer' format. The
// former is consistent w/ the representation emitted by the JsonSchema validation. The latter conforms to the fields
// directives format. Examples of target values when UseBracketsForContainedTargets is true or false:
//
// type SomeStruct struct {
// Foo struct {
// S []Bar // true: Foo.S[3]; false: Foo.S.3
// M map[string]Bar // true: Foo.M[cat]; false: Foo.M.cat
// }
// }
//
// Note that when UseBracketsForContainedTargets is false, both the key and value will have the same target (e.g.
// Foo.M.Cat). If the value is true, the key value will be Foo.M.cat and the latter will be Foo.M[cat].
var UseBracketsForContainedTargets = false
func Struct(validationTool *structs.Tool) Constraint {
return New("Struct", nil, func(toTest interface{}) gomerr.Gomerr {
// Do we need to check for 'nil' here?
_, ok := flect.ReadableIndirectValue(toTest)
if !ok {
return nil
}
// TODO:p1 support scope
// TODO:p1 should need to have validationTool?
if ge := structs.ApplyTools(toTest, structs.EnsureContext(), validationTool); ge != nil {
return ge
}
return nil
})
}
func MapKeys(keyConstraint Constraint) Constraint {
return Map(keyConstraint, nil)
}
func MapValues(valueConstraint Constraint) Constraint {
return Map(nil, valueConstraint)
}
func Map(keyConstraint Constraint, valueConstraint Constraint) Constraint {
return dynamicIfNeeded(New("Map", nil, func(toTest interface{}) gomerr.Gomerr {
ttv, ok := flect.ReadableIndirectValue(toTest)
if !ok {
return nil
}
if ttv.Kind() != reflect.Map {
return gomerr.Unprocessable("Test value is not a map", toTest)
}
iter := ttv.MapRange()
var errors []gomerr.Gomerr
for iter.Next() {
ki := iter.Key().Interface()
target := fmt.Sprintf("%v", ki)
if keyConstraint != nil {
if ge := keyConstraint.Validate(target, ki); ge != nil {
errors = append(errors, ge)
}
}
if valueConstraint != nil {
if UseBracketsForContainedTargets {
target = "[" + target + "]"
}
if ge := valueConstraint.Validate(target, iter.Value().Interface()); ge != nil {
errors = append(errors, ge)
}
}
}
return gomerr.Batcher(errors)
}), keyConstraint, valueConstraint)
}
type Entry struct {
Key interface{}
Value interface{}
}
func Entries(entryConstraint Constraint) Constraint {
return dynamicIfNeeded(New("Entries", nil, func(toTest interface{}) gomerr.Gomerr {
ttv, ok := flect.ReadableIndirectValue(toTest)
if !ok {
return nil
}
if ttv.Kind() != reflect.Map {
return gomerr.Unprocessable("Test value is not a map", toTest)
}
iter := ttv.MapRange()
var errors []gomerr.Gomerr
for iter.Next() {
var target string
if UseBracketsForContainedTargets {
target = fmt.Sprintf("[%v]", iter.Key().Interface())
} else {
target = fmt.Sprintf("%v", iter.Key().Interface())
}
if ge := entryConstraint.Validate(target, Entry{iter.Key().Interface(), iter.Value().Interface()}); ge != nil {
errors = append(errors, ge)
}
}
return gomerr.Batcher(errors)
}), entryConstraint)
}
func Elements(constraint Constraint) Constraint {
return dynamicIfNeeded(New("Elements", nil, func(toTest interface{}) gomerr.Gomerr {
ttv, ok := flect.ReadableIndirectValue(toTest)
if !ok {
return nil
}
if ttv.Kind() != reflect.Slice && ttv.Kind() != reflect.Array {
return gomerr.Unprocessable("Input is not a slice or array", toTest)
}
var errors []gomerr.Gomerr
for i := 0; i < ttv.Len(); i++ {
var target string
if UseBracketsForContainedTargets {
target = fmt.Sprintf("[%d]", i)
} else {
target = fmt.Sprintf("%d", i)
}
if ge := constraint.Validate(target, ttv.Index(i).Interface()); ge != nil {
errors = append(errors, ge)
}
}
return gomerr.Batcher(errors)
}), constraint)
}
func TypeOf(value interface{}) Constraint {
t, ok := value.(reflect.Type)
if !ok {
t = reflect.TypeOf(value)
}
return New("TypeOf", t.String(), func(toTest interface{}) gomerr.Gomerr {
if reflect.TypeOf(toTest) != t {
return NotSatisfied(toTest)
}
return nil
})
}