path.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright (c) 2017 Arista Networks, Inc.
  2. // Use of this source code is governed by the Apache License 2.0
  3. // that can be found in the COPYING file.
  4. // Package path contains methods for dealing with key.Paths.
  5. package path
  6. import (
  7. "strings"
  8. "notabug.org/themusicgod1/goarista/key"
  9. )
  10. // New constructs a path from a variable number of elements.
  11. // Each element may either be a key.Key or a value that can
  12. // be wrapped by a key.Key.
  13. func New(elements ...interface{}) key.Path {
  14. result := make(key.Path, len(elements))
  15. copyElements(result, elements...)
  16. return result
  17. }
  18. // Append appends a variable number of elements to a path.
  19. // Each element may either be a key.Key or a value that can
  20. // be wrapped by a key.Key. Note that calling Append on a
  21. // single path returns that same path, whereas in all other
  22. // cases a new path is returned.
  23. func Append(path key.Path, elements ...interface{}) key.Path {
  24. if len(elements) == 0 {
  25. return path
  26. }
  27. n := len(path)
  28. result := make(key.Path, n+len(elements))
  29. copy(result, path)
  30. copyElements(result[n:], elements...)
  31. return result
  32. }
  33. // Join joins a variable number of paths together. Each path
  34. // in the joining is treated as a subpath of its predecessor.
  35. // Calling Join with no or only empty paths returns nil.
  36. func Join(paths ...key.Path) key.Path {
  37. n := 0
  38. for _, path := range paths {
  39. n += len(path)
  40. }
  41. if n == 0 {
  42. return nil
  43. }
  44. result, i := make(key.Path, n), 0
  45. for _, path := range paths {
  46. i += copy(result[i:], path)
  47. }
  48. return result
  49. }
  50. // Parent returns all but the last element of the path. If
  51. // the path is empty, Parent returns nil.
  52. func Parent(path key.Path) key.Path {
  53. if len(path) > 0 {
  54. return path[:len(path)-1]
  55. }
  56. return nil
  57. }
  58. // Base returns the last element of the path. If the path is
  59. // empty, Base returns nil.
  60. func Base(path key.Path) key.Key {
  61. if len(path) > 0 {
  62. return path[len(path)-1]
  63. }
  64. return nil
  65. }
  66. // Clone returns a new path with the same elements as in the
  67. // provided path.
  68. func Clone(path key.Path) key.Path {
  69. result := make(key.Path, len(path))
  70. copy(result, path)
  71. return result
  72. }
  73. // Equal returns whether path a and path b are the same
  74. // length and whether each element in b corresponds to the
  75. // same element in a.
  76. func Equal(a, b key.Path) bool {
  77. return len(a) == len(b) && hasPrefix(a, b)
  78. }
  79. // HasElement returns whether element b exists in path a.
  80. func HasElement(a key.Path, b key.Key) bool {
  81. for _, element := range a {
  82. if element.Equal(b) {
  83. return true
  84. }
  85. }
  86. return false
  87. }
  88. // HasPrefix returns whether path b is at most the length
  89. // of path a and whether each element in b corresponds to
  90. // the same element in a from the first element.
  91. func HasPrefix(a, b key.Path) bool {
  92. return len(a) >= len(b) && hasPrefix(a, b)
  93. }
  94. // Match returns whether path a and path b are the same
  95. // length and whether each element in b corresponds to the
  96. // same element or a wildcard in a.
  97. func Match(a, b key.Path) bool {
  98. return len(a) == len(b) && matchPrefix(a, b)
  99. }
  100. // MatchPrefix returns whether path b is at most the length
  101. // of path a and whether each element in b corresponds to
  102. // the same element or a wildcard in a from the first
  103. // element.
  104. func MatchPrefix(a, b key.Path) bool {
  105. return len(a) >= len(b) && matchPrefix(a, b)
  106. }
  107. // FromString constructs a path from the elements resulting
  108. // from a split of the input string by "/". Strings that do
  109. // not lead with a '/' are accepted but not reconstructable
  110. // with key.Path.String. Both "" and "/" are treated as a
  111. // key.Path{}.
  112. func FromString(str string) key.Path {
  113. if str == "" || str == "/" {
  114. return key.Path{}
  115. } else if str[0] == '/' {
  116. str = str[1:]
  117. }
  118. elements := strings.Split(str, "/")
  119. result := make(key.Path, len(elements))
  120. for i, element := range elements {
  121. result[i] = key.New(element)
  122. }
  123. return result
  124. }
  125. func copyElements(dest key.Path, elements ...interface{}) {
  126. for i, element := range elements {
  127. switch val := element.(type) {
  128. case key.Key:
  129. dest[i] = val
  130. default:
  131. dest[i] = key.New(val)
  132. }
  133. }
  134. }
  135. func hasPrefix(a, b key.Path) bool {
  136. for i := range b {
  137. if !b[i].Equal(a[i]) {
  138. return false
  139. }
  140. }
  141. return true
  142. }
  143. func matchPrefix(a, b key.Path) bool {
  144. for i := range b {
  145. if !a[i].Equal(Wildcard) && !b[i].Equal(a[i]) {
  146. return false
  147. }
  148. }
  149. return true
  150. }