path.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /*
  2. Copyright 2018 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 fieldpath
  14. import (
  15. "fmt"
  16. "strings"
  17. "sigs.k8s.io/structured-merge-diff/v4/value"
  18. )
  19. // Path describes how to select a potentially deeply-nested child field given a
  20. // containing object.
  21. type Path []PathElement
  22. func (fp Path) String() string {
  23. strs := make([]string, len(fp))
  24. for i := range fp {
  25. strs[i] = fp[i].String()
  26. }
  27. return strings.Join(strs, "")
  28. }
  29. // Equals returns true if the two paths are equivalent.
  30. func (fp Path) Equals(fp2 Path) bool {
  31. if len(fp) != len(fp2) {
  32. return false
  33. }
  34. for i := range fp {
  35. if !fp[i].Equals(fp2[i]) {
  36. return false
  37. }
  38. }
  39. return true
  40. }
  41. // Less provides a lexical order for Paths.
  42. func (fp Path) Compare(rhs Path) int {
  43. i := 0
  44. for {
  45. if i >= len(fp) && i >= len(rhs) {
  46. // Paths are the same length and all items are equal.
  47. return 0
  48. }
  49. if i >= len(fp) {
  50. // LHS is shorter.
  51. return -1
  52. }
  53. if i >= len(rhs) {
  54. // RHS is shorter.
  55. return 1
  56. }
  57. if c := fp[i].Compare(rhs[i]); c != 0 {
  58. return c
  59. }
  60. // The items are equal; continue.
  61. i++
  62. }
  63. }
  64. func (fp Path) Copy() Path {
  65. new := make(Path, len(fp))
  66. copy(new, fp)
  67. return new
  68. }
  69. // MakePath constructs a Path. The parts may be PathElements, ints, strings.
  70. func MakePath(parts ...interface{}) (Path, error) {
  71. var fp Path
  72. for _, p := range parts {
  73. switch t := p.(type) {
  74. case PathElement:
  75. fp = append(fp, t)
  76. case int:
  77. // TODO: Understand schema and object and convert this to the
  78. // FieldSpecifier below if appropriate.
  79. fp = append(fp, PathElement{Index: &t})
  80. case string:
  81. fp = append(fp, PathElement{FieldName: &t})
  82. case *value.FieldList:
  83. if len(*t) == 0 {
  84. return nil, fmt.Errorf("associative list key type path elements must have at least one key (got zero)")
  85. }
  86. fp = append(fp, PathElement{Key: t})
  87. case value.Value:
  88. // TODO: understand schema and verify that this is a set type
  89. // TODO: make a copy of t
  90. fp = append(fp, PathElement{Value: &t})
  91. default:
  92. return nil, fmt.Errorf("unable to make %#v into a path element", p)
  93. }
  94. }
  95. return fp, nil
  96. }
  97. // MakePathOrDie panics if parts can't be turned into a path. Good for things
  98. // that are known at complie time.
  99. func MakePathOrDie(parts ...interface{}) Path {
  100. fp, err := MakePath(parts...)
  101. if err != nil {
  102. panic(err)
  103. }
  104. return fp
  105. }