nested.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package yaml
  14. import (
  15. "fmt"
  16. "gopkg.in/yaml.v3"
  17. )
  18. // ValueInMapping finds the value node with the corresponding string key
  19. // in the given mapping node. If the given node is not a mapping, an
  20. // error will be returned.
  21. func ValueInMapping(root *yaml.Node, key string) (*yaml.Node, error) {
  22. if root.Kind != yaml.MappingNode {
  23. return nil, fmt.Errorf("unexpected non-mapping node")
  24. }
  25. for i := 0; i < len(root.Content)/2; i++ {
  26. keyNode := root.Content[i*2]
  27. if keyNode.Value == key {
  28. return root.Content[i*2+1], nil
  29. }
  30. }
  31. return nil, nil
  32. }
  33. // asCloseAsPossible goes as deep on the given path as possible, returning the
  34. // last node that existed from the given path in the given tree of mapping
  35. // nodes, as well as the rest of the path that could not be fetched, if any.
  36. func asCloseAsPossible(root *yaml.Node, path ...string) (*yaml.Node, []string, error) {
  37. if root == nil {
  38. return nil, path, nil
  39. }
  40. if root.Kind == yaml.DocumentNode && len(root.Content) > 0 {
  41. root = root.Content[0]
  42. }
  43. currNode := root
  44. for ; len(path) > 0; path = path[1:] {
  45. if currNode.Kind != yaml.MappingNode {
  46. return nil, nil, fmt.Errorf("unexpected non-mapping (%v) before path %v", currNode.Kind, path)
  47. }
  48. nextNode, err := ValueInMapping(currNode, path[0])
  49. if err != nil {
  50. return nil, nil, fmt.Errorf("unable to get next node in path %v: %w", path, err)
  51. }
  52. if nextNode == nil {
  53. // we're as close as possible
  54. break
  55. }
  56. currNode = nextNode
  57. }
  58. return currNode, path, nil
  59. }
  60. // GetNode gets the node at the given path in the given sequence of mapping
  61. // nodes, or, if it doesn't exist, returning false.
  62. func GetNode(root *yaml.Node, path ...string) (*yaml.Node, bool, error) {
  63. resNode, restPath, err := asCloseAsPossible(root, path...)
  64. if err != nil {
  65. return nil, false, err
  66. }
  67. // more path means the node didn't exist
  68. if len(restPath) != 0 {
  69. return nil, false, nil
  70. }
  71. return resNode, true, nil
  72. }