github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/path/path.go (about) 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 5 // Package path contains methods for dealing with key.Paths. 6 package path 7 8 import ( 9 "strings" 10 11 "github.com/aristanetworks/goarista/key" 12 ) 13 14 // New constructs a path from a variable number of elements. 15 // Each element may either be a key.Key or a value that can 16 // be wrapped by a key.Key. 17 func New(elements ...interface{}) key.Path { 18 return appendElements(nil, elements...) 19 } 20 21 // Append appends a variable number of elements to a path. 22 // Each element may either be a key.Key or a value that can 23 // be wrapped by a key.Key. Note that calling Append on a 24 // single path returns that same path, whereas in all other 25 // cases a new path is returned. 26 func Append(path key.Path, elements ...interface{}) key.Path { 27 return appendElements(path, elements...) 28 } 29 30 // Join joins a variable number of paths together. Each path 31 // in the joining is treated as a subpath of its predecessor. 32 // Calling Join with no or only empty paths returns nil. 33 func Join(paths ...key.Path) key.Path { 34 n := 0 35 for _, path := range paths { 36 n += len(path) 37 } 38 if n == 0 { 39 return nil 40 } 41 result, i := make(key.Path, n), 0 42 for _, path := range paths { 43 i += copy(result[i:], path) 44 } 45 return result 46 } 47 48 // Parent returns all but the last element of the path. If 49 // the path is empty, Parent returns nil. 50 func Parent(path key.Path) key.Path { 51 if len(path) > 0 { 52 return path[:len(path)-1] 53 } 54 return nil 55 } 56 57 // Base returns the last element of the path. If the path is 58 // empty, Base returns nil. 59 func Base(path key.Path) key.Key { 60 if len(path) > 0 { 61 return path[len(path)-1] 62 } 63 return nil 64 } 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 74 // Equal returns whether path a and path b are the same 75 // length and whether each element in b corresponds to the 76 // same element in a. 77 func Equal(a, b key.Path) bool { 78 return len(a) == len(b) && hasPrefix(a, b) 79 } 80 81 // HasElement returns whether element b exists in path a. 82 func HasElement(a key.Path, b key.Key) bool { 83 for _, element := range a { 84 if element.Equal(b) { 85 return true 86 } 87 } 88 return false 89 } 90 91 // HasPrefix returns whether path b is a prefix of path a. 92 // It checks that b is at most the length of path a and 93 // whether each element in b corresponds to the same element 94 // in a from the first element. 95 func HasPrefix(a, b key.Path) bool { 96 return len(a) >= len(b) && hasPrefix(a, b) 97 } 98 99 // Match returns whether path a and path b are the same 100 // length and whether each element in b corresponds to the 101 // same element or a wildcard in a. 102 func Match(a, b key.Path) bool { 103 return len(a) == len(b) && matchPrefix(a, b) 104 } 105 106 // MatchPrefix returns whether path b is a prefix of path a 107 // where path a may contain wildcards. 108 // It checks that b is at most the length of path a and 109 // whether each element in b corresponds to the same element 110 // or a wildcard in a from the first element. 111 func MatchPrefix(a, b key.Path) bool { 112 return len(a) >= len(b) && matchPrefix(a, b) 113 } 114 115 // FromString constructs a path from the elements resulting 116 // from a split of the input string by "/". Strings that do 117 // not lead with a '/' are accepted but not reconstructable 118 // with key.Path.String. Both "" and "/" are treated as a 119 // key.Path{}. 120 func FromString(str string) key.Path { 121 if str == "" || str == "/" { 122 return key.Path{} 123 } else if str[0] == '/' { 124 str = str[1:] 125 } 126 elements := strings.Split(str, "/") 127 result := make(key.Path, len(elements)) 128 for i, element := range elements { 129 result[i] = key.New(element) 130 } 131 return result 132 } 133 134 // appendElements makes a copy of dest when elements is non-empty and 135 // then appends elements to the copy and returns it. 136 func appendElements(dest key.Path, elements ...interface{}) key.Path { 137 if len(elements) == 0 { 138 return dest 139 } 140 clone := make(key.Path, len(dest), len(dest)+len(elements)) 141 copy(clone, dest) 142 dest = clone 143 for _, element := range elements { 144 switch val := element.(type) { 145 case key.Key: 146 dest = append(dest, val) 147 case []key.Key: 148 dest = append(dest, val...) 149 case key.Path: 150 dest = append(dest, val...) 151 case []string: 152 for i := range val { 153 dest = append(dest, key.New(val[i])) 154 } 155 case []key.Path: 156 for i := range val { 157 dest = append(dest, val[i]...) 158 } 159 default: 160 dest = append(dest, key.New(val)) 161 } 162 } 163 return dest 164 } 165 166 func hasPrefix(a, b key.Path) bool { 167 for i := range b { 168 if !b[i].Equal(a[i]) { 169 return false 170 } 171 } 172 return true 173 } 174 175 func matchPrefix(a, b key.Path) bool { 176 for i := range b { 177 if !a[i].Equal(Wildcard) && !b[i].Equal(a[i]) { 178 return false 179 } 180 } 181 return true 182 }