1
16
17 package typed
18
19 import (
20 "sync"
21
22 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
23 "sigs.k8s.io/structured-merge-diff/v4/schema"
24 "sigs.k8s.io/structured-merge-diff/v4/value"
25 )
26
27 var vPool = sync.Pool{
28 New: func() interface{} { return &validatingObjectWalker{} },
29 }
30
31 func (tv TypedValue) walker() *validatingObjectWalker {
32 v := vPool.Get().(*validatingObjectWalker)
33 v.value = tv.value
34 v.schema = tv.schema
35 v.typeRef = tv.typeRef
36 v.allowDuplicates = false
37 if v.allocator == nil {
38 v.allocator = value.NewFreelistAllocator()
39 }
40 return v
41 }
42
43 func (v *validatingObjectWalker) finished() {
44 v.schema = nil
45 v.typeRef = schema.TypeRef{}
46 vPool.Put(v)
47 }
48
49 type validatingObjectWalker struct {
50 value value.Value
51 schema *schema.Schema
52 typeRef schema.TypeRef
53
54
55 allowDuplicates bool
56
57
58 spareWalkers *[]*validatingObjectWalker
59 allocator value.Allocator
60 }
61
62 func (v *validatingObjectWalker) prepareDescent(tr schema.TypeRef) *validatingObjectWalker {
63 if v.spareWalkers == nil {
64
65 v.spareWalkers = &[]*validatingObjectWalker{}
66 }
67 var v2 *validatingObjectWalker
68 if n := len(*v.spareWalkers); n > 0 {
69 v2, *v.spareWalkers = (*v.spareWalkers)[n-1], (*v.spareWalkers)[:n-1]
70 } else {
71 v2 = &validatingObjectWalker{}
72 }
73 *v2 = *v
74 v2.typeRef = tr
75 return v2
76 }
77
78 func (v *validatingObjectWalker) finishDescent(v2 *validatingObjectWalker) {
79
80
81 *v.spareWalkers = append(*v.spareWalkers, v2)
82 }
83
84 func (v *validatingObjectWalker) validate(prefixFn func() string) ValidationErrors {
85 return resolveSchema(v.schema, v.typeRef, v.value, v).WithLazyPrefix(prefixFn)
86 }
87
88 func validateScalar(t *schema.Scalar, v value.Value, prefix string) (errs ValidationErrors) {
89 if v == nil {
90 return nil
91 }
92 if v.IsNull() {
93 return nil
94 }
95 switch *t {
96 case schema.Numeric:
97 if !v.IsFloat() && !v.IsInt() {
98
99 return errorf("%vexpected numeric (int or float), got %T", prefix, v.Unstructured())
100 }
101 case schema.String:
102 if !v.IsString() {
103 return errorf("%vexpected string, got %#v", prefix, v)
104 }
105 case schema.Boolean:
106 if !v.IsBool() {
107 return errorf("%vexpected boolean, got %v", prefix, v)
108 }
109 case schema.Untyped:
110 if !v.IsFloat() && !v.IsInt() && !v.IsString() && !v.IsBool() {
111 return errorf("%vexpected any scalar, got %v", prefix, v)
112 }
113 default:
114 return errorf("%vunexpected scalar type in schema: %v", prefix, *t)
115 }
116 return nil
117 }
118
119 func (v *validatingObjectWalker) doScalar(t *schema.Scalar) ValidationErrors {
120 if errs := validateScalar(t, v.value, ""); len(errs) > 0 {
121 return errs
122 }
123 return nil
124 }
125
126 func (v *validatingObjectWalker) visitListItems(t *schema.List, list value.List) (errs ValidationErrors) {
127 observedKeys := fieldpath.MakePathElementSet(list.Length())
128 for i := 0; i < list.Length(); i++ {
129 child := list.AtUsing(v.allocator, i)
130 defer v.allocator.Free(child)
131 var pe fieldpath.PathElement
132 if t.ElementRelationship != schema.Associative {
133 pe.Index = &i
134 } else {
135 var err error
136 pe, err = listItemToPathElement(v.allocator, v.schema, t, child)
137 if err != nil {
138 errs = append(errs, errorf("element %v: %v", i, err.Error())...)
139
140
141
142 return
143 }
144 if observedKeys.Has(pe) && !v.allowDuplicates {
145 errs = append(errs, errorf("duplicate entries for key %v", pe.String())...)
146 }
147 observedKeys.Insert(pe)
148 }
149 v2 := v.prepareDescent(t.ElementType)
150 v2.value = child
151 errs = append(errs, v2.validate(pe.String)...)
152 v.finishDescent(v2)
153 }
154 return errs
155 }
156
157 func (v *validatingObjectWalker) doList(t *schema.List) (errs ValidationErrors) {
158 list, err := listValue(v.allocator, v.value)
159 if err != nil {
160 return errorf(err.Error())
161 }
162
163 if list == nil {
164 return nil
165 }
166
167 defer v.allocator.Free(list)
168 errs = v.visitListItems(t, list)
169
170 return errs
171 }
172
173 func (v *validatingObjectWalker) visitMapItems(t *schema.Map, m value.Map) (errs ValidationErrors) {
174 m.IterateUsing(v.allocator, func(key string, val value.Value) bool {
175 pe := fieldpath.PathElement{FieldName: &key}
176 tr := t.ElementType
177 if sf, ok := t.FindField(key); ok {
178 tr = sf.Type
179 } else if (t.ElementType == schema.TypeRef{}) {
180 errs = append(errs, errorf("field not declared in schema").WithPrefix(pe.String())...)
181 return false
182 }
183 v2 := v.prepareDescent(tr)
184 v2.value = val
185
186 errs = append(errs, v2.validate(func() string { return pe.String() })...)
187 v.finishDescent(v2)
188 return true
189 })
190 return errs
191 }
192
193 func (v *validatingObjectWalker) doMap(t *schema.Map) (errs ValidationErrors) {
194 m, err := mapValue(v.allocator, v.value)
195 if err != nil {
196 return errorf(err.Error())
197 }
198 if m == nil {
199 return nil
200 }
201 defer v.allocator.Free(m)
202 errs = v.visitMapItems(t, m)
203
204 return errs
205 }
206
View as plain text