]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package hcl |
2 | ||
3 | import ( | |
4 | "errors" | |
5 | "fmt" | |
6 | "reflect" | |
7 | "sort" | |
8 | "strconv" | |
9 | "strings" | |
10 | ||
11 | "github.com/hashicorp/hcl/hcl/ast" | |
12 | "github.com/hashicorp/hcl/hcl/parser" | |
13 | "github.com/hashicorp/hcl/hcl/token" | |
14 | ) | |
15 | ||
16 | // This is the tag to use with structures to have settings for HCL | |
17 | const tagName = "hcl" | |
18 | ||
19 | var ( | |
20 | // nodeType holds a reference to the type of ast.Node | |
21 | nodeType reflect.Type = findNodeType() | |
22 | ) | |
23 | ||
24 | // Unmarshal accepts a byte slice as input and writes the | |
25 | // data to the value pointed to by v. | |
26 | func Unmarshal(bs []byte, v interface{}) error { | |
27 | root, err := parse(bs) | |
28 | if err != nil { | |
29 | return err | |
30 | } | |
31 | ||
32 | return DecodeObject(v, root) | |
33 | } | |
34 | ||
35 | // Decode reads the given input and decodes it into the structure | |
36 | // given by `out`. | |
37 | func Decode(out interface{}, in string) error { | |
38 | obj, err := Parse(in) | |
39 | if err != nil { | |
40 | return err | |
41 | } | |
42 | ||
43 | return DecodeObject(out, obj) | |
44 | } | |
45 | ||
46 | // DecodeObject is a lower-level version of Decode. It decodes a | |
47 | // raw Object into the given output. | |
48 | func DecodeObject(out interface{}, n ast.Node) error { | |
49 | val := reflect.ValueOf(out) | |
50 | if val.Kind() != reflect.Ptr { | |
51 | return errors.New("result must be a pointer") | |
52 | } | |
53 | ||
54 | // If we have the file, we really decode the root node | |
55 | if f, ok := n.(*ast.File); ok { | |
56 | n = f.Node | |
57 | } | |
58 | ||
59 | var d decoder | |
60 | return d.decode("root", n, val.Elem()) | |
61 | } | |
62 | ||
63 | type decoder struct { | |
64 | stack []reflect.Kind | |
65 | } | |
66 | ||
67 | func (d *decoder) decode(name string, node ast.Node, result reflect.Value) error { | |
68 | k := result | |
69 | ||
70 | // If we have an interface with a valid value, we use that | |
71 | // for the check. | |
72 | if result.Kind() == reflect.Interface { | |
73 | elem := result.Elem() | |
74 | if elem.IsValid() { | |
75 | k = elem | |
76 | } | |
77 | } | |
78 | ||
79 | // Push current onto stack unless it is an interface. | |
80 | if k.Kind() != reflect.Interface { | |
81 | d.stack = append(d.stack, k.Kind()) | |
82 | ||
83 | // Schedule a pop | |
84 | defer func() { | |
85 | d.stack = d.stack[:len(d.stack)-1] | |
86 | }() | |
87 | } | |
88 | ||
89 | switch k.Kind() { | |
90 | case reflect.Bool: | |
91 | return d.decodeBool(name, node, result) | |
92 | case reflect.Float64: | |
93 | return d.decodeFloat(name, node, result) | |
94 | case reflect.Int, reflect.Int32, reflect.Int64: | |
95 | return d.decodeInt(name, node, result) | |
96 | case reflect.Interface: | |
97 | // When we see an interface, we make our own thing | |
98 | return d.decodeInterface(name, node, result) | |
99 | case reflect.Map: | |
100 | return d.decodeMap(name, node, result) | |
101 | case reflect.Ptr: | |
102 | return d.decodePtr(name, node, result) | |
103 | case reflect.Slice: | |
104 | return d.decodeSlice(name, node, result) | |
105 | case reflect.String: | |
106 | return d.decodeString(name, node, result) | |
107 | case reflect.Struct: | |
108 | return d.decodeStruct(name, node, result) | |
109 | default: | |
110 | return &parser.PosError{ | |
111 | Pos: node.Pos(), | |
112 | Err: fmt.Errorf("%s: unknown kind to decode into: %s", name, k.Kind()), | |
113 | } | |
114 | } | |
115 | } | |
116 | ||
117 | func (d *decoder) decodeBool(name string, node ast.Node, result reflect.Value) error { | |
118 | switch n := node.(type) { | |
119 | case *ast.LiteralType: | |
120 | if n.Token.Type == token.BOOL { | |
121 | v, err := strconv.ParseBool(n.Token.Text) | |
122 | if err != nil { | |
123 | return err | |
124 | } | |
125 | ||
126 | result.Set(reflect.ValueOf(v)) | |
127 | return nil | |
128 | } | |
129 | } | |
130 | ||
131 | return &parser.PosError{ | |
132 | Pos: node.Pos(), | |
133 | Err: fmt.Errorf("%s: unknown type %T", name, node), | |
134 | } | |
135 | } | |
136 | ||
137 | func (d *decoder) decodeFloat(name string, node ast.Node, result reflect.Value) error { | |
138 | switch n := node.(type) { | |
139 | case *ast.LiteralType: | |
140 | if n.Token.Type == token.FLOAT { | |
141 | v, err := strconv.ParseFloat(n.Token.Text, 64) | |
142 | if err != nil { | |
143 | return err | |
144 | } | |
145 | ||
146 | result.Set(reflect.ValueOf(v)) | |
147 | return nil | |
148 | } | |
149 | } | |
150 | ||
151 | return &parser.PosError{ | |
152 | Pos: node.Pos(), | |
153 | Err: fmt.Errorf("%s: unknown type %T", name, node), | |
154 | } | |
155 | } | |
156 | ||
157 | func (d *decoder) decodeInt(name string, node ast.Node, result reflect.Value) error { | |
158 | switch n := node.(type) { | |
159 | case *ast.LiteralType: | |
160 | switch n.Token.Type { | |
161 | case token.NUMBER: | |
162 | v, err := strconv.ParseInt(n.Token.Text, 0, 0) | |
163 | if err != nil { | |
164 | return err | |
165 | } | |
166 | ||
167 | if result.Kind() == reflect.Interface { | |
168 | result.Set(reflect.ValueOf(int(v))) | |
169 | } else { | |
170 | result.SetInt(v) | |
171 | } | |
172 | return nil | |
173 | case token.STRING: | |
174 | v, err := strconv.ParseInt(n.Token.Value().(string), 0, 0) | |
175 | if err != nil { | |
176 | return err | |
177 | } | |
178 | ||
179 | if result.Kind() == reflect.Interface { | |
180 | result.Set(reflect.ValueOf(int(v))) | |
181 | } else { | |
182 | result.SetInt(v) | |
183 | } | |
184 | return nil | |
185 | } | |
186 | } | |
187 | ||
188 | return &parser.PosError{ | |
189 | Pos: node.Pos(), | |
190 | Err: fmt.Errorf("%s: unknown type %T", name, node), | |
191 | } | |
192 | } | |
193 | ||
194 | func (d *decoder) decodeInterface(name string, node ast.Node, result reflect.Value) error { | |
195 | // When we see an ast.Node, we retain the value to enable deferred decoding. | |
196 | // Very useful in situations where we want to preserve ast.Node information | |
197 | // like Pos | |
198 | if result.Type() == nodeType && result.CanSet() { | |
199 | result.Set(reflect.ValueOf(node)) | |
200 | return nil | |
201 | } | |
202 | ||
203 | var set reflect.Value | |
204 | redecode := true | |
205 | ||
206 | // For testing types, ObjectType should just be treated as a list. We | |
207 | // set this to a temporary var because we want to pass in the real node. | |
208 | testNode := node | |
209 | if ot, ok := node.(*ast.ObjectType); ok { | |
210 | testNode = ot.List | |
211 | } | |
212 | ||
213 | switch n := testNode.(type) { | |
214 | case *ast.ObjectList: | |
215 | // If we're at the root or we're directly within a slice, then we | |
216 | // decode objects into map[string]interface{}, otherwise we decode | |
217 | // them into lists. | |
218 | if len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice { | |
219 | var temp map[string]interface{} | |
220 | tempVal := reflect.ValueOf(temp) | |
221 | result := reflect.MakeMap( | |
222 | reflect.MapOf( | |
223 | reflect.TypeOf(""), | |
224 | tempVal.Type().Elem())) | |
225 | ||
226 | set = result | |
227 | } else { | |
228 | var temp []map[string]interface{} | |
229 | tempVal := reflect.ValueOf(temp) | |
230 | result := reflect.MakeSlice( | |
231 | reflect.SliceOf(tempVal.Type().Elem()), 0, len(n.Items)) | |
232 | set = result | |
233 | } | |
234 | case *ast.ObjectType: | |
235 | // If we're at the root or we're directly within a slice, then we | |
236 | // decode objects into map[string]interface{}, otherwise we decode | |
237 | // them into lists. | |
238 | if len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice { | |
239 | var temp map[string]interface{} | |
240 | tempVal := reflect.ValueOf(temp) | |
241 | result := reflect.MakeMap( | |
242 | reflect.MapOf( | |
243 | reflect.TypeOf(""), | |
244 | tempVal.Type().Elem())) | |
245 | ||
246 | set = result | |
247 | } else { | |
248 | var temp []map[string]interface{} | |
249 | tempVal := reflect.ValueOf(temp) | |
250 | result := reflect.MakeSlice( | |
251 | reflect.SliceOf(tempVal.Type().Elem()), 0, 1) | |
252 | set = result | |
253 | } | |
254 | case *ast.ListType: | |
255 | var temp []interface{} | |
256 | tempVal := reflect.ValueOf(temp) | |
257 | result := reflect.MakeSlice( | |
258 | reflect.SliceOf(tempVal.Type().Elem()), 0, 0) | |
259 | set = result | |
260 | case *ast.LiteralType: | |
261 | switch n.Token.Type { | |
262 | case token.BOOL: | |
263 | var result bool | |
264 | set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) | |
265 | case token.FLOAT: | |
266 | var result float64 | |
267 | set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) | |
268 | case token.NUMBER: | |
269 | var result int | |
270 | set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) | |
271 | case token.STRING, token.HEREDOC: | |
272 | set = reflect.Indirect(reflect.New(reflect.TypeOf(""))) | |
273 | default: | |
274 | return &parser.PosError{ | |
275 | Pos: node.Pos(), | |
276 | Err: fmt.Errorf("%s: cannot decode into interface: %T", name, node), | |
277 | } | |
278 | } | |
279 | default: | |
280 | return fmt.Errorf( | |
281 | "%s: cannot decode into interface: %T", | |
282 | name, node) | |
283 | } | |
284 | ||
285 | // Set the result to what its supposed to be, then reset | |
286 | // result so we don't reflect into this method anymore. | |
287 | result.Set(set) | |
288 | ||
289 | if redecode { | |
290 | // Revisit the node so that we can use the newly instantiated | |
291 | // thing and populate it. | |
292 | if err := d.decode(name, node, result); err != nil { | |
293 | return err | |
294 | } | |
295 | } | |
296 | ||
297 | return nil | |
298 | } | |
299 | ||
300 | func (d *decoder) decodeMap(name string, node ast.Node, result reflect.Value) error { | |
301 | if item, ok := node.(*ast.ObjectItem); ok { | |
302 | node = &ast.ObjectList{Items: []*ast.ObjectItem{item}} | |
303 | } | |
304 | ||
305 | if ot, ok := node.(*ast.ObjectType); ok { | |
306 | node = ot.List | |
307 | } | |
308 | ||
309 | n, ok := node.(*ast.ObjectList) | |
310 | if !ok { | |
311 | return &parser.PosError{ | |
312 | Pos: node.Pos(), | |
313 | Err: fmt.Errorf("%s: not an object type for map (%T)", name, node), | |
314 | } | |
315 | } | |
316 | ||
317 | // If we have an interface, then we can address the interface, | |
318 | // but not the slice itself, so get the element but set the interface | |
319 | set := result | |
320 | if result.Kind() == reflect.Interface { | |
321 | result = result.Elem() | |
322 | } | |
323 | ||
324 | resultType := result.Type() | |
325 | resultElemType := resultType.Elem() | |
326 | resultKeyType := resultType.Key() | |
327 | if resultKeyType.Kind() != reflect.String { | |
328 | return &parser.PosError{ | |
329 | Pos: node.Pos(), | |
330 | Err: fmt.Errorf("%s: map must have string keys", name), | |
331 | } | |
332 | } | |
333 | ||
334 | // Make a map if it is nil | |
335 | resultMap := result | |
336 | if result.IsNil() { | |
337 | resultMap = reflect.MakeMap( | |
338 | reflect.MapOf(resultKeyType, resultElemType)) | |
339 | } | |
340 | ||
341 | // Go through each element and decode it. | |
342 | done := make(map[string]struct{}) | |
343 | for _, item := range n.Items { | |
344 | if item.Val == nil { | |
345 | continue | |
346 | } | |
347 | ||
348 | // github.com/hashicorp/terraform/issue/5740 | |
349 | if len(item.Keys) == 0 { | |
350 | return &parser.PosError{ | |
351 | Pos: node.Pos(), | |
352 | Err: fmt.Errorf("%s: map must have string keys", name), | |
353 | } | |
354 | } | |
355 | ||
356 | // Get the key we're dealing with, which is the first item | |
357 | keyStr := item.Keys[0].Token.Value().(string) | |
358 | ||
359 | // If we've already processed this key, then ignore it | |
360 | if _, ok := done[keyStr]; ok { | |
361 | continue | |
362 | } | |
363 | ||
364 | // Determine the value. If we have more than one key, then we | |
365 | // get the objectlist of only these keys. | |
366 | itemVal := item.Val | |
367 | if len(item.Keys) > 1 { | |
368 | itemVal = n.Filter(keyStr) | |
369 | done[keyStr] = struct{}{} | |
370 | } | |
371 | ||
372 | // Make the field name | |
373 | fieldName := fmt.Sprintf("%s.%s", name, keyStr) | |
374 | ||
375 | // Get the key/value as reflection values | |
376 | key := reflect.ValueOf(keyStr) | |
377 | val := reflect.Indirect(reflect.New(resultElemType)) | |
378 | ||
379 | // If we have a pre-existing value in the map, use that | |
380 | oldVal := resultMap.MapIndex(key) | |
381 | if oldVal.IsValid() { | |
382 | val.Set(oldVal) | |
383 | } | |
384 | ||
385 | // Decode! | |
386 | if err := d.decode(fieldName, itemVal, val); err != nil { | |
387 | return err | |
388 | } | |
389 | ||
390 | // Set the value on the map | |
391 | resultMap.SetMapIndex(key, val) | |
392 | } | |
393 | ||
394 | // Set the final map if we can | |
395 | set.Set(resultMap) | |
396 | return nil | |
397 | } | |
398 | ||
399 | func (d *decoder) decodePtr(name string, node ast.Node, result reflect.Value) error { | |
400 | // Create an element of the concrete (non pointer) type and decode | |
401 | // into that. Then set the value of the pointer to this type. | |
402 | resultType := result.Type() | |
403 | resultElemType := resultType.Elem() | |
404 | val := reflect.New(resultElemType) | |
405 | if err := d.decode(name, node, reflect.Indirect(val)); err != nil { | |
406 | return err | |
407 | } | |
408 | ||
409 | result.Set(val) | |
410 | return nil | |
411 | } | |
412 | ||
413 | func (d *decoder) decodeSlice(name string, node ast.Node, result reflect.Value) error { | |
414 | // If we have an interface, then we can address the interface, | |
415 | // but not the slice itself, so get the element but set the interface | |
416 | set := result | |
417 | if result.Kind() == reflect.Interface { | |
418 | result = result.Elem() | |
419 | } | |
420 | // Create the slice if it isn't nil | |
421 | resultType := result.Type() | |
422 | resultElemType := resultType.Elem() | |
423 | if result.IsNil() { | |
424 | resultSliceType := reflect.SliceOf(resultElemType) | |
425 | result = reflect.MakeSlice( | |
426 | resultSliceType, 0, 0) | |
427 | } | |
428 | ||
429 | // Figure out the items we'll be copying into the slice | |
430 | var items []ast.Node | |
431 | switch n := node.(type) { | |
432 | case *ast.ObjectList: | |
433 | items = make([]ast.Node, len(n.Items)) | |
434 | for i, item := range n.Items { | |
435 | items[i] = item | |
436 | } | |
437 | case *ast.ObjectType: | |
438 | items = []ast.Node{n} | |
439 | case *ast.ListType: | |
440 | items = n.List | |
441 | default: | |
442 | return &parser.PosError{ | |
443 | Pos: node.Pos(), | |
444 | Err: fmt.Errorf("unknown slice type: %T", node), | |
445 | } | |
446 | } | |
447 | ||
448 | for i, item := range items { | |
449 | fieldName := fmt.Sprintf("%s[%d]", name, i) | |
450 | ||
451 | // Decode | |
452 | val := reflect.Indirect(reflect.New(resultElemType)) | |
453 | ||
454 | // if item is an object that was decoded from ambiguous JSON and | |
455 | // flattened, make sure it's expanded if it needs to decode into a | |
456 | // defined structure. | |
457 | item := expandObject(item, val) | |
458 | ||
459 | if err := d.decode(fieldName, item, val); err != nil { | |
460 | return err | |
461 | } | |
462 | ||
463 | // Append it onto the slice | |
464 | result = reflect.Append(result, val) | |
465 | } | |
466 | ||
467 | set.Set(result) | |
468 | return nil | |
469 | } | |
470 | ||
471 | // expandObject detects if an ambiguous JSON object was flattened to a List which | |
472 | // should be decoded into a struct, and expands the ast to properly deocode. | |
473 | func expandObject(node ast.Node, result reflect.Value) ast.Node { | |
474 | item, ok := node.(*ast.ObjectItem) | |
475 | if !ok { | |
476 | return node | |
477 | } | |
478 | ||
479 | elemType := result.Type() | |
480 | ||
481 | // our target type must be a struct | |
482 | switch elemType.Kind() { | |
483 | case reflect.Ptr: | |
484 | switch elemType.Elem().Kind() { | |
485 | case reflect.Struct: | |
486 | //OK | |
487 | default: | |
488 | return node | |
489 | } | |
490 | case reflect.Struct: | |
491 | //OK | |
492 | default: | |
493 | return node | |
494 | } | |
495 | ||
496 | // A list value will have a key and field name. If it had more fields, | |
497 | // it wouldn't have been flattened. | |
498 | if len(item.Keys) != 2 { | |
499 | return node | |
500 | } | |
501 | ||
502 | keyToken := item.Keys[0].Token | |
503 | item.Keys = item.Keys[1:] | |
504 | ||
505 | // we need to un-flatten the ast enough to decode | |
506 | newNode := &ast.ObjectItem{ | |
507 | Keys: []*ast.ObjectKey{ | |
508 | &ast.ObjectKey{ | |
509 | Token: keyToken, | |
510 | }, | |
511 | }, | |
512 | Val: &ast.ObjectType{ | |
513 | List: &ast.ObjectList{ | |
514 | Items: []*ast.ObjectItem{item}, | |
515 | }, | |
516 | }, | |
517 | } | |
518 | ||
519 | return newNode | |
520 | } | |
521 | ||
522 | func (d *decoder) decodeString(name string, node ast.Node, result reflect.Value) error { | |
523 | switch n := node.(type) { | |
524 | case *ast.LiteralType: | |
525 | switch n.Token.Type { | |
526 | case token.NUMBER: | |
527 | result.Set(reflect.ValueOf(n.Token.Text).Convert(result.Type())) | |
528 | return nil | |
529 | case token.STRING, token.HEREDOC: | |
530 | result.Set(reflect.ValueOf(n.Token.Value()).Convert(result.Type())) | |
531 | return nil | |
532 | } | |
533 | } | |
534 | ||
535 | return &parser.PosError{ | |
536 | Pos: node.Pos(), | |
537 | Err: fmt.Errorf("%s: unknown type for string %T", name, node), | |
538 | } | |
539 | } | |
540 | ||
541 | func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) error { | |
542 | var item *ast.ObjectItem | |
543 | if it, ok := node.(*ast.ObjectItem); ok { | |
544 | item = it | |
545 | node = it.Val | |
546 | } | |
547 | ||
548 | if ot, ok := node.(*ast.ObjectType); ok { | |
549 | node = ot.List | |
550 | } | |
551 | ||
552 | // Handle the special case where the object itself is a literal. Previously | |
553 | // the yacc parser would always ensure top-level elements were arrays. The new | |
554 | // parser does not make the same guarantees, thus we need to convert any | |
555 | // top-level literal elements into a list. | |
556 | if _, ok := node.(*ast.LiteralType); ok && item != nil { | |
557 | node = &ast.ObjectList{Items: []*ast.ObjectItem{item}} | |
558 | } | |
559 | ||
560 | list, ok := node.(*ast.ObjectList) | |
561 | if !ok { | |
562 | return &parser.PosError{ | |
563 | Pos: node.Pos(), | |
564 | Err: fmt.Errorf("%s: not an object type for struct (%T)", name, node), | |
565 | } | |
566 | } | |
567 | ||
568 | // This slice will keep track of all the structs we'll be decoding. | |
569 | // There can be more than one struct if there are embedded structs | |
570 | // that are squashed. | |
571 | structs := make([]reflect.Value, 1, 5) | |
572 | structs[0] = result | |
573 | ||
574 | // Compile the list of all the fields that we're going to be decoding | |
575 | // from all the structs. | |
576 | fields := make(map[*reflect.StructField]reflect.Value) | |
577 | for len(structs) > 0 { | |
578 | structVal := structs[0] | |
579 | structs = structs[1:] | |
580 | ||
581 | structType := structVal.Type() | |
582 | for i := 0; i < structType.NumField(); i++ { | |
583 | fieldType := structType.Field(i) | |
584 | tagParts := strings.Split(fieldType.Tag.Get(tagName), ",") | |
585 | ||
586 | // Ignore fields with tag name "-" | |
587 | if tagParts[0] == "-" { | |
588 | continue | |
589 | } | |
590 | ||
591 | if fieldType.Anonymous { | |
592 | fieldKind := fieldType.Type.Kind() | |
593 | if fieldKind != reflect.Struct { | |
594 | return &parser.PosError{ | |
595 | Pos: node.Pos(), | |
596 | Err: fmt.Errorf("%s: unsupported type to struct: %s", | |
597 | fieldType.Name, fieldKind), | |
598 | } | |
599 | } | |
600 | ||
601 | // We have an embedded field. We "squash" the fields down | |
602 | // if specified in the tag. | |
603 | squash := false | |
604 | for _, tag := range tagParts[1:] { | |
605 | if tag == "squash" { | |
606 | squash = true | |
607 | break | |
608 | } | |
609 | } | |
610 | ||
611 | if squash { | |
612 | structs = append( | |
613 | structs, result.FieldByName(fieldType.Name)) | |
614 | continue | |
615 | } | |
616 | } | |
617 | ||
618 | // Normal struct field, store it away | |
619 | fields[&fieldType] = structVal.Field(i) | |
620 | } | |
621 | } | |
622 | ||
623 | usedKeys := make(map[string]struct{}) | |
624 | decodedFields := make([]string, 0, len(fields)) | |
625 | decodedFieldsVal := make([]reflect.Value, 0) | |
626 | unusedKeysVal := make([]reflect.Value, 0) | |
627 | for fieldType, field := range fields { | |
628 | if !field.IsValid() { | |
629 | // This should never happen | |
630 | panic("field is not valid") | |
631 | } | |
632 | ||
633 | // If we can't set the field, then it is unexported or something, | |
634 | // and we just continue onwards. | |
635 | if !field.CanSet() { | |
636 | continue | |
637 | } | |
638 | ||
639 | fieldName := fieldType.Name | |
640 | ||
641 | tagValue := fieldType.Tag.Get(tagName) | |
642 | tagParts := strings.SplitN(tagValue, ",", 2) | |
643 | if len(tagParts) >= 2 { | |
644 | switch tagParts[1] { | |
645 | case "decodedFields": | |
646 | decodedFieldsVal = append(decodedFieldsVal, field) | |
647 | continue | |
648 | case "key": | |
649 | if item == nil { | |
650 | return &parser.PosError{ | |
651 | Pos: node.Pos(), | |
652 | Err: fmt.Errorf("%s: %s asked for 'key', impossible", | |
653 | name, fieldName), | |
654 | } | |
655 | } | |
656 | ||
657 | field.SetString(item.Keys[0].Token.Value().(string)) | |
658 | continue | |
659 | case "unusedKeys": | |
660 | unusedKeysVal = append(unusedKeysVal, field) | |
661 | continue | |
662 | } | |
663 | } | |
664 | ||
665 | if tagParts[0] != "" { | |
666 | fieldName = tagParts[0] | |
667 | } | |
668 | ||
669 | // Determine the element we'll use to decode. If it is a single | |
670 | // match (only object with the field), then we decode it exactly. | |
671 | // If it is a prefix match, then we decode the matches. | |
672 | filter := list.Filter(fieldName) | |
673 | ||
674 | prefixMatches := filter.Children() | |
675 | matches := filter.Elem() | |
676 | if len(matches.Items) == 0 && len(prefixMatches.Items) == 0 { | |
677 | continue | |
678 | } | |
679 | ||
680 | // Track the used key | |
681 | usedKeys[fieldName] = struct{}{} | |
682 | ||
683 | // Create the field name and decode. We range over the elements | |
684 | // because we actually want the value. | |
685 | fieldName = fmt.Sprintf("%s.%s", name, fieldName) | |
686 | if len(prefixMatches.Items) > 0 { | |
687 | if err := d.decode(fieldName, prefixMatches, field); err != nil { | |
688 | return err | |
689 | } | |
690 | } | |
691 | for _, match := range matches.Items { | |
692 | var decodeNode ast.Node = match.Val | |
693 | if ot, ok := decodeNode.(*ast.ObjectType); ok { | |
694 | decodeNode = &ast.ObjectList{Items: ot.List.Items} | |
695 | } | |
696 | ||
697 | if err := d.decode(fieldName, decodeNode, field); err != nil { | |
698 | return err | |
699 | } | |
700 | } | |
701 | ||
702 | decodedFields = append(decodedFields, fieldType.Name) | |
703 | } | |
704 | ||
705 | if len(decodedFieldsVal) > 0 { | |
706 | // Sort it so that it is deterministic | |
707 | sort.Strings(decodedFields) | |
708 | ||
709 | for _, v := range decodedFieldsVal { | |
710 | v.Set(reflect.ValueOf(decodedFields)) | |
711 | } | |
712 | } | |
713 | ||
714 | return nil | |
715 | } | |
716 | ||
717 | // findNodeType returns the type of ast.Node | |
718 | func findNodeType() reflect.Type { | |
719 | var nodeContainer struct { | |
720 | Node ast.Node | |
721 | } | |
722 | value := reflect.ValueOf(nodeContainer).FieldByName("Node") | |
723 | return value.Type() | |
724 | } |