]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package gocty |
2 | ||
3 | import ( | |
107c1cdb | 4 | "math" |
15c0b25d AP |
5 | "math/big" |
6 | "reflect" | |
7 | ||
15c0b25d AP |
8 | "github.com/zclconf/go-cty/cty" |
9 | ) | |
10 | ||
11 | // FromCtyValue assigns a cty.Value to a reflect.Value, which must be a pointer, | |
12 | // using a fixed set of conversion rules. | |
13 | // | |
14 | // This function considers its audience to be the creator of the cty Value | |
15 | // given, and thus the error messages it generates are (unlike with ToCtyValue) | |
16 | // presented in cty terminology that is generally appropriate to return to | |
17 | // end-users in applications where cty data structures are built from | |
18 | // user-provided configuration. In particular this means that if incorrect | |
19 | // target types are provided by the calling application the resulting error | |
20 | // messages are likely to be confusing, since we assume that the given target | |
21 | // type is correct and the cty.Value is where the error lies. | |
22 | // | |
23 | // If an error is returned, the target data structure may have been partially | |
24 | // populated, but the degree to which this is true is an implementation | |
25 | // detail that the calling application should not rely on. | |
26 | // | |
27 | // The function will panic if given a non-pointer as the Go value target, | |
28 | // since that is considered to be a bug in the calling program. | |
29 | func FromCtyValue(val cty.Value, target interface{}) error { | |
30 | tVal := reflect.ValueOf(target) | |
31 | if tVal.Kind() != reflect.Ptr { | |
32 | panic("target value is not a pointer") | |
33 | } | |
34 | if tVal.IsNil() { | |
35 | panic("target value is nil pointer") | |
36 | } | |
37 | ||
38 | // 'path' starts off as empty but will grow for each level of recursive | |
39 | // call we make, so by the time fromCtyValue returns it is likely to have | |
40 | // unused capacity on the end of it, depending on how deeply-recursive | |
41 | // the given cty.Value is. | |
42 | path := make(cty.Path, 0) | |
43 | return fromCtyValue(val, tVal, path) | |
44 | } | |
45 | ||
46 | func fromCtyValue(val cty.Value, target reflect.Value, path cty.Path) error { | |
47 | ty := val.Type() | |
48 | ||
49 | deepTarget := fromCtyPopulatePtr(target, false) | |
50 | ||
51 | // If we're decoding into a cty.Value then we just pass through the | |
52 | // value as-is, to enable partial decoding. This is the only situation | |
53 | // where unknown values are permitted. | |
54 | if deepTarget.Kind() == reflect.Struct && deepTarget.Type().AssignableTo(valueType) { | |
55 | deepTarget.Set(reflect.ValueOf(val)) | |
56 | return nil | |
57 | } | |
58 | ||
59 | // Lists and maps can be nil without indirection, but everything else | |
60 | // requires a pointer and we set it immediately to nil. | |
61 | // We also make an exception for capsule types because we want to handle | |
62 | // pointers specially for these. | |
63 | // (fromCtyList and fromCtyMap must therefore deal with val.IsNull, while | |
64 | // other types can assume no nulls after this point.) | |
65 | if val.IsNull() && !val.Type().IsListType() && !val.Type().IsMapType() && !val.Type().IsCapsuleType() { | |
66 | target = fromCtyPopulatePtr(target, true) | |
67 | if target.Kind() != reflect.Ptr { | |
68 | return path.NewErrorf("null value is not allowed") | |
69 | } | |
70 | ||
71 | target.Set(reflect.Zero(target.Type())) | |
72 | return nil | |
73 | } | |
74 | ||
75 | target = deepTarget | |
76 | ||
77 | if !val.IsKnown() { | |
78 | return path.NewErrorf("value must be known") | |
79 | } | |
80 | ||
81 | switch ty { | |
82 | case cty.Bool: | |
83 | return fromCtyBool(val, target, path) | |
84 | case cty.Number: | |
85 | return fromCtyNumber(val, target, path) | |
86 | case cty.String: | |
87 | return fromCtyString(val, target, path) | |
88 | } | |
89 | ||
90 | switch { | |
91 | case ty.IsListType(): | |
92 | return fromCtyList(val, target, path) | |
93 | case ty.IsMapType(): | |
94 | return fromCtyMap(val, target, path) | |
95 | case ty.IsSetType(): | |
96 | return fromCtySet(val, target, path) | |
97 | case ty.IsObjectType(): | |
98 | return fromCtyObject(val, target, path) | |
99 | case ty.IsTupleType(): | |
100 | return fromCtyTuple(val, target, path) | |
101 | case ty.IsCapsuleType(): | |
102 | return fromCtyCapsule(val, target, path) | |
103 | } | |
104 | ||
105 | // We should never fall out here; reaching here indicates a bug in this | |
106 | // function. | |
107 | return path.NewErrorf("unsupported source type %#v", ty) | |
108 | } | |
109 | ||
110 | func fromCtyBool(val cty.Value, target reflect.Value, path cty.Path) error { | |
111 | switch target.Kind() { | |
112 | ||
113 | case reflect.Bool: | |
107c1cdb | 114 | target.SetBool(val.True()) |
15c0b25d AP |
115 | return nil |
116 | ||
117 | default: | |
118 | return likelyRequiredTypesError(path, target) | |
119 | ||
120 | } | |
121 | } | |
122 | ||
123 | func fromCtyNumber(val cty.Value, target reflect.Value, path cty.Path) error { | |
124 | bf := val.AsBigFloat() | |
125 | ||
126 | switch target.Kind() { | |
127 | ||
128 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
129 | return fromCtyNumberInt(bf, target, path) | |
130 | ||
131 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |
132 | return fromCtyNumberUInt(bf, target, path) | |
133 | ||
134 | case reflect.Float32, reflect.Float64: | |
135 | return fromCtyNumberFloat(bf, target, path) | |
136 | ||
137 | case reflect.Struct: | |
138 | return fromCtyNumberBig(bf, target, path) | |
139 | ||
140 | default: | |
141 | return likelyRequiredTypesError(path, target) | |
142 | ||
143 | } | |
144 | } | |
145 | ||
146 | func fromCtyNumberInt(bf *big.Float, target reflect.Value, path cty.Path) error { | |
147 | // Doing this with switch rather than << arithmetic because << with | |
148 | // result >32-bits is not portable to 32-bit systems. | |
149 | var min int64 | |
150 | var max int64 | |
151 | switch target.Type().Bits() { | |
152 | case 8: | |
153 | min = math.MinInt8 | |
154 | max = math.MaxInt8 | |
155 | case 16: | |
156 | min = math.MinInt16 | |
157 | max = math.MaxInt16 | |
158 | case 32: | |
159 | min = math.MinInt32 | |
160 | max = math.MaxInt32 | |
161 | case 64: | |
162 | min = math.MinInt64 | |
163 | max = math.MaxInt64 | |
164 | default: | |
165 | panic("weird number of bits in target int") | |
166 | } | |
167 | ||
168 | iv, accuracy := bf.Int64() | |
169 | if accuracy != big.Exact || iv < min || iv > max { | |
170 | return path.NewErrorf("value must be a whole number, between %d and %d", min, max) | |
171 | } | |
172 | ||
107c1cdb | 173 | target.SetInt(iv) |
15c0b25d AP |
174 | return nil |
175 | } | |
176 | ||
177 | func fromCtyNumberUInt(bf *big.Float, target reflect.Value, path cty.Path) error { | |
178 | // Doing this with switch rather than << arithmetic because << with | |
179 | // result >32-bits is not portable to 32-bit systems. | |
180 | var max uint64 | |
181 | switch target.Type().Bits() { | |
182 | case 8: | |
183 | max = math.MaxUint8 | |
184 | case 16: | |
185 | max = math.MaxUint16 | |
186 | case 32: | |
187 | max = math.MaxUint32 | |
188 | case 64: | |
189 | max = math.MaxUint64 | |
190 | default: | |
191 | panic("weird number of bits in target uint") | |
192 | } | |
193 | ||
194 | iv, accuracy := bf.Uint64() | |
195 | if accuracy != big.Exact || iv > max { | |
196 | return path.NewErrorf("value must be a whole number, between 0 and %d inclusive", max) | |
197 | } | |
198 | ||
107c1cdb | 199 | target.SetUint(iv) |
15c0b25d AP |
200 | return nil |
201 | } | |
202 | ||
203 | func fromCtyNumberFloat(bf *big.Float, target reflect.Value, path cty.Path) error { | |
204 | switch target.Kind() { | |
107c1cdb | 205 | case reflect.Float32, reflect.Float64: |
15c0b25d AP |
206 | fv, accuracy := bf.Float64() |
207 | if accuracy != big.Exact { | |
208 | // We allow the precision to be truncated as part of our conversion, | |
209 | // but we don't want to silently introduce infinities. | |
210 | if math.IsInf(fv, 0) { | |
211 | return path.NewErrorf("value must be between %f and %f inclusive", -math.MaxFloat64, math.MaxFloat64) | |
212 | } | |
213 | } | |
107c1cdb | 214 | target.SetFloat(fv) |
15c0b25d AP |
215 | return nil |
216 | default: | |
217 | panic("unsupported kind of float") | |
218 | } | |
219 | } | |
220 | ||
221 | func fromCtyNumberBig(bf *big.Float, target reflect.Value, path cty.Path) error { | |
222 | switch { | |
223 | ||
107c1cdb | 224 | case bigFloatType.ConvertibleTo(target.Type()): |
15c0b25d | 225 | // Easy! |
107c1cdb | 226 | target.Set(reflect.ValueOf(bf).Elem().Convert(target.Type())) |
15c0b25d AP |
227 | return nil |
228 | ||
107c1cdb | 229 | case bigIntType.ConvertibleTo(target.Type()): |
15c0b25d AP |
230 | bi, accuracy := bf.Int(nil) |
231 | if accuracy != big.Exact { | |
232 | return path.NewErrorf("value must be a whole number") | |
233 | } | |
107c1cdb | 234 | target.Set(reflect.ValueOf(bi).Elem().Convert(target.Type())) |
15c0b25d AP |
235 | return nil |
236 | ||
237 | default: | |
238 | return likelyRequiredTypesError(path, target) | |
239 | } | |
240 | } | |
241 | ||
242 | func fromCtyString(val cty.Value, target reflect.Value, path cty.Path) error { | |
243 | switch target.Kind() { | |
15c0b25d | 244 | case reflect.String: |
107c1cdb | 245 | target.SetString(val.AsString()) |
15c0b25d AP |
246 | return nil |
247 | ||
248 | default: | |
249 | return likelyRequiredTypesError(path, target) | |
250 | ||
251 | } | |
252 | } | |
253 | ||
254 | func fromCtyList(val cty.Value, target reflect.Value, path cty.Path) error { | |
255 | switch target.Kind() { | |
256 | ||
257 | case reflect.Slice: | |
258 | if val.IsNull() { | |
259 | target.Set(reflect.Zero(target.Type())) | |
260 | return nil | |
261 | } | |
262 | ||
263 | length := val.LengthInt() | |
264 | tv := reflect.MakeSlice(target.Type(), length, length) | |
265 | ||
266 | path = append(path, nil) | |
267 | ||
268 | i := 0 | |
269 | var err error | |
270 | val.ForEachElement(func(key cty.Value, val cty.Value) bool { | |
271 | path[len(path)-1] = cty.IndexStep{ | |
272 | Key: cty.NumberIntVal(int64(i)), | |
273 | } | |
274 | ||
275 | targetElem := tv.Index(i) | |
276 | err = fromCtyValue(val, targetElem, path) | |
277 | if err != nil { | |
278 | return true | |
279 | } | |
280 | ||
281 | i++ | |
282 | return false | |
283 | }) | |
284 | if err != nil { | |
285 | return err | |
286 | } | |
287 | ||
288 | path = path[:len(path)-1] | |
289 | ||
290 | target.Set(tv) | |
291 | return nil | |
292 | ||
293 | case reflect.Array: | |
294 | if val.IsNull() { | |
295 | return path.NewErrorf("null value is not allowed") | |
296 | } | |
297 | ||
298 | length := val.LengthInt() | |
299 | if length != target.Len() { | |
300 | return path.NewErrorf("must be a list of length %d", target.Len()) | |
301 | } | |
302 | ||
303 | path = append(path, nil) | |
304 | ||
305 | i := 0 | |
306 | var err error | |
307 | val.ForEachElement(func(key cty.Value, val cty.Value) bool { | |
308 | path[len(path)-1] = cty.IndexStep{ | |
309 | Key: cty.NumberIntVal(int64(i)), | |
310 | } | |
311 | ||
312 | targetElem := target.Index(i) | |
313 | err = fromCtyValue(val, targetElem, path) | |
314 | if err != nil { | |
315 | return true | |
316 | } | |
317 | ||
318 | i++ | |
319 | return false | |
320 | }) | |
321 | if err != nil { | |
322 | return err | |
323 | } | |
324 | ||
325 | path = path[:len(path)-1] | |
326 | ||
327 | return nil | |
328 | ||
329 | default: | |
330 | return likelyRequiredTypesError(path, target) | |
331 | ||
332 | } | |
333 | } | |
334 | ||
335 | func fromCtyMap(val cty.Value, target reflect.Value, path cty.Path) error { | |
336 | ||
337 | switch target.Kind() { | |
338 | ||
339 | case reflect.Map: | |
340 | if val.IsNull() { | |
341 | target.Set(reflect.Zero(target.Type())) | |
342 | return nil | |
343 | } | |
344 | ||
345 | tv := reflect.MakeMap(target.Type()) | |
346 | et := target.Type().Elem() | |
347 | ||
348 | path = append(path, nil) | |
349 | ||
350 | var err error | |
351 | val.ForEachElement(func(key cty.Value, val cty.Value) bool { | |
352 | path[len(path)-1] = cty.IndexStep{ | |
353 | Key: key, | |
354 | } | |
355 | ||
356 | ks := key.AsString() | |
357 | ||
358 | targetElem := reflect.New(et) | |
359 | err = fromCtyValue(val, targetElem, path) | |
360 | ||
361 | tv.SetMapIndex(reflect.ValueOf(ks), targetElem.Elem()) | |
362 | ||
363 | return err != nil | |
364 | }) | |
365 | if err != nil { | |
366 | return err | |
367 | } | |
368 | ||
369 | path = path[:len(path)-1] | |
370 | ||
371 | target.Set(tv) | |
372 | return nil | |
373 | ||
374 | default: | |
375 | return likelyRequiredTypesError(path, target) | |
376 | ||
377 | } | |
378 | } | |
379 | ||
380 | func fromCtySet(val cty.Value, target reflect.Value, path cty.Path) error { | |
381 | switch target.Kind() { | |
382 | ||
383 | case reflect.Slice: | |
384 | if val.IsNull() { | |
385 | target.Set(reflect.Zero(target.Type())) | |
386 | return nil | |
387 | } | |
388 | ||
389 | length := val.LengthInt() | |
390 | tv := reflect.MakeSlice(target.Type(), length, length) | |
391 | ||
392 | i := 0 | |
393 | var err error | |
394 | val.ForEachElement(func(key cty.Value, val cty.Value) bool { | |
395 | targetElem := tv.Index(i) | |
396 | err = fromCtyValue(val, targetElem, path) | |
397 | if err != nil { | |
398 | return true | |
399 | } | |
400 | ||
401 | i++ | |
402 | return false | |
403 | }) | |
404 | if err != nil { | |
405 | return err | |
406 | } | |
407 | ||
408 | target.Set(tv) | |
409 | return nil | |
410 | ||
411 | case reflect.Array: | |
412 | if val.IsNull() { | |
413 | return path.NewErrorf("null value is not allowed") | |
414 | } | |
415 | ||
416 | length := val.LengthInt() | |
417 | if length != target.Len() { | |
418 | return path.NewErrorf("must be a set of length %d", target.Len()) | |
419 | } | |
420 | ||
421 | i := 0 | |
422 | var err error | |
423 | val.ForEachElement(func(key cty.Value, val cty.Value) bool { | |
424 | targetElem := target.Index(i) | |
425 | err = fromCtyValue(val, targetElem, path) | |
426 | if err != nil { | |
427 | return true | |
428 | } | |
429 | ||
430 | i++ | |
431 | return false | |
432 | }) | |
433 | if err != nil { | |
434 | return err | |
435 | } | |
436 | ||
437 | return nil | |
438 | ||
439 | // TODO: decode into set.Set instance | |
440 | ||
441 | default: | |
442 | return likelyRequiredTypesError(path, target) | |
443 | ||
444 | } | |
445 | } | |
446 | ||
447 | func fromCtyObject(val cty.Value, target reflect.Value, path cty.Path) error { | |
448 | ||
449 | switch target.Kind() { | |
450 | ||
451 | case reflect.Struct: | |
452 | ||
453 | attrTypes := val.Type().AttributeTypes() | |
454 | targetFields := structTagIndices(target.Type()) | |
455 | ||
456 | path = append(path, nil) | |
457 | ||
458 | for k, i := range targetFields { | |
459 | if _, exists := attrTypes[k]; !exists { | |
460 | // If the field in question isn't able to represent nil, | |
461 | // that's an error. | |
462 | fk := target.Field(i).Kind() | |
463 | switch fk { | |
464 | case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface: | |
465 | // okay | |
466 | default: | |
467 | return path.NewErrorf("missing required attribute %q", k) | |
468 | } | |
469 | } | |
470 | } | |
471 | ||
472 | for k := range attrTypes { | |
473 | path[len(path)-1] = cty.GetAttrStep{ | |
474 | Name: k, | |
475 | } | |
476 | ||
477 | fieldIdx, exists := targetFields[k] | |
478 | if !exists { | |
479 | return path.NewErrorf("unsupported attribute %q", k) | |
480 | } | |
481 | ||
482 | ev := val.GetAttr(k) | |
483 | ||
484 | targetField := target.Field(fieldIdx) | |
485 | err := fromCtyValue(ev, targetField, path) | |
486 | if err != nil { | |
487 | return err | |
488 | } | |
489 | } | |
490 | ||
491 | path = path[:len(path)-1] | |
492 | ||
493 | return nil | |
494 | ||
495 | default: | |
496 | return likelyRequiredTypesError(path, target) | |
497 | ||
498 | } | |
499 | } | |
500 | ||
501 | func fromCtyTuple(val cty.Value, target reflect.Value, path cty.Path) error { | |
502 | ||
503 | switch target.Kind() { | |
504 | ||
505 | case reflect.Struct: | |
506 | ||
507 | elemTypes := val.Type().TupleElementTypes() | |
508 | fieldCount := target.Type().NumField() | |
509 | ||
510 | if fieldCount != len(elemTypes) { | |
511 | return path.NewErrorf("a tuple of %d elements is required", fieldCount) | |
512 | } | |
513 | ||
514 | path = append(path, nil) | |
515 | ||
516 | for i := range elemTypes { | |
517 | path[len(path)-1] = cty.IndexStep{ | |
518 | Key: cty.NumberIntVal(int64(i)), | |
519 | } | |
520 | ||
521 | ev := val.Index(cty.NumberIntVal(int64(i))) | |
522 | ||
523 | targetField := target.Field(i) | |
524 | err := fromCtyValue(ev, targetField, path) | |
525 | if err != nil { | |
526 | return err | |
527 | } | |
528 | } | |
529 | ||
530 | path = path[:len(path)-1] | |
531 | ||
532 | return nil | |
533 | ||
534 | default: | |
535 | return likelyRequiredTypesError(path, target) | |
536 | ||
537 | } | |
538 | } | |
539 | ||
540 | func fromCtyCapsule(val cty.Value, target reflect.Value, path cty.Path) error { | |
541 | ||
542 | if target.Kind() == reflect.Ptr { | |
543 | // Walk through indirection until we get to the last pointer, | |
544 | // which we might set to null below. | |
545 | target = fromCtyPopulatePtr(target, true) | |
546 | ||
547 | if val.IsNull() { | |
548 | target.Set(reflect.Zero(target.Type())) | |
549 | return nil | |
550 | } | |
551 | ||
552 | // Since a capsule contains a pointer to an object, we'll preserve | |
553 | // that pointer on the way out and thus allow the caller to recover | |
554 | // the original object, rather than a copy of it. | |
555 | ||
556 | eType := val.Type().EncapsulatedType() | |
557 | ||
558 | if !eType.AssignableTo(target.Elem().Type()) { | |
559 | // Our interface contract promises that we won't expose Go | |
560 | // implementation details in error messages, so we need to keep | |
561 | // this vague. This can only arise if a calling application has | |
562 | // more than one capsule type in play and a user mixes them up. | |
563 | return path.NewErrorf("incorrect type %s", val.Type().FriendlyName()) | |
564 | } | |
565 | ||
566 | target.Set(reflect.ValueOf(val.EncapsulatedValue())) | |
567 | ||
568 | return nil | |
569 | } else { | |
570 | if val.IsNull() { | |
571 | return path.NewErrorf("null value is not allowed") | |
572 | } | |
573 | ||
574 | // If our target isn't a pointer then we will attempt to copy | |
575 | // the encapsulated value into it. | |
576 | ||
577 | eType := val.Type().EncapsulatedType() | |
578 | ||
579 | if !eType.AssignableTo(target.Type()) { | |
580 | // Our interface contract promises that we won't expose Go | |
581 | // implementation details in error messages, so we need to keep | |
582 | // this vague. This can only arise if a calling application has | |
583 | // more than one capsule type in play and a user mixes them up. | |
584 | return path.NewErrorf("incorrect type %s", val.Type().FriendlyName()) | |
585 | } | |
586 | ||
587 | // We know that EncapsulatedValue is always a pointer, so we | |
588 | // can safely call .Elem on its reflect.Value. | |
589 | target.Set(reflect.ValueOf(val.EncapsulatedValue()).Elem()) | |
590 | ||
591 | return nil | |
592 | } | |
593 | ||
594 | } | |
595 | ||
596 | // fromCtyPopulatePtr recognizes when target is a pointer type and allocates | |
597 | // a value to assign to that pointer, which it returns. | |
598 | // | |
599 | // If the given value has multiple levels of indirection, like **int, these | |
600 | // will be processed in turn so that the return value is guaranteed to be | |
601 | // a non-pointer. | |
602 | // | |
603 | // As an exception, if decodingNull is true then the returned value will be | |
604 | // the final level of pointer, if any, so that the caller can assign it | |
605 | // as nil to represent a null value. If the given target value is not a pointer | |
606 | // at all then the returned value will be just the given target, so the caller | |
607 | // must test if the returned value is a pointer before trying to assign nil | |
608 | // to it. | |
609 | func fromCtyPopulatePtr(target reflect.Value, decodingNull bool) reflect.Value { | |
610 | for { | |
611 | if target.Kind() == reflect.Interface && !target.IsNil() { | |
612 | e := target.Elem() | |
613 | if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { | |
614 | target = e | |
615 | } | |
616 | } | |
617 | ||
618 | if target.Kind() != reflect.Ptr { | |
619 | break | |
620 | } | |
621 | ||
622 | // Stop early if we're decodingNull and we've found our last indirection | |
623 | if target.Elem().Kind() != reflect.Ptr && decodingNull && target.CanSet() { | |
624 | break | |
625 | } | |
626 | ||
627 | if target.IsNil() { | |
628 | target.Set(reflect.New(target.Type().Elem())) | |
629 | } | |
630 | ||
631 | target = target.Elem() | |
632 | } | |
633 | return target | |
634 | } | |
635 | ||
636 | // likelyRequiredTypesError returns an error that states which types are | |
637 | // acceptable by making some assumptions about what types we support for | |
638 | // each target Go kind. It's not a precise science but it allows us to return | |
639 | // an error message that is cty-user-oriented rather than Go-oriented. | |
640 | // | |
641 | // Generally these error messages should be a matter of last resort, since | |
642 | // the calling application should be validating user-provided value types | |
643 | // before decoding anyway. | |
644 | func likelyRequiredTypesError(path cty.Path, target reflect.Value) error { | |
645 | switch target.Kind() { | |
646 | ||
647 | case reflect.Bool: | |
648 | return path.NewErrorf("bool value is required") | |
649 | ||
650 | case reflect.String: | |
651 | return path.NewErrorf("string value is required") | |
652 | ||
653 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
654 | fallthrough | |
655 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |
656 | fallthrough | |
657 | case reflect.Float32, reflect.Float64: | |
658 | return path.NewErrorf("number value is required") | |
659 | ||
660 | case reflect.Slice, reflect.Array: | |
661 | return path.NewErrorf("list or set value is required") | |
662 | ||
663 | case reflect.Map: | |
664 | return path.NewErrorf("map or object value is required") | |
665 | ||
666 | case reflect.Struct: | |
667 | switch { | |
668 | ||
669 | case target.Type().AssignableTo(bigFloatType) || target.Type().AssignableTo(bigIntType): | |
670 | return path.NewErrorf("number value is required") | |
671 | ||
672 | case target.Type().AssignableTo(setType): | |
673 | return path.NewErrorf("set or list value is required") | |
674 | ||
675 | default: | |
676 | return path.NewErrorf("object or tuple value is required") | |
677 | ||
678 | } | |
679 | ||
680 | default: | |
681 | // We should avoid getting into this path, since this error | |
682 | // message is rather useless. | |
683 | return path.NewErrorf("incorrect type") | |
684 | ||
685 | } | |
686 | } |