github.com/avfs/avfs@v0.33.1-0.20240303173310-c6ba67c33eb7/pathiterator.go (about) 1 // 2 // Copyright 2021 The AVFS authors 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 package avfs 18 19 import "strings" 20 21 // PathIterator iterates through an absolute path. 22 // It returns each part of the path in successive calls to Next. 23 // The volume name (for Windows) is not considered as part of the path 24 // it is returned by VolumeName. 25 // 26 // Sample code : 27 // 28 // pi := NewPathIterator(vfs, path) 29 // for pi.Next() { 30 // fmt.Println(pi.Part()) 31 // } 32 // 33 // The path below shows the different results of the PathIterator methods 34 // when thirdPart is the current Part : 35 // 36 // /firstPart/secondPart/thirdPart/fourthPart/fifthPart 37 // |- Part --| 38 // Start End 39 // |------- Left -------| |------ Right ------| 40 // |----- LeftPart ---------------| 41 // |----------- RightPart -------| 42 type PathIterator[T VFSBase] struct { 43 vfs T 44 path string 45 start int 46 end int 47 volumeNameLen int 48 pathSeparator uint8 49 } 50 51 // NewPathIterator creates a new path iterator from an absolute path. 52 func NewPathIterator[T VFSBase](vfs T, path string) *PathIterator[T] { 53 pi := PathIterator[T]{path: path} 54 pi.vfs = vfs 55 pi.volumeNameLen = VolumeNameLen(vfs, path) 56 pi.pathSeparator = vfs.PathSeparator() 57 pi.Reset() 58 59 return &pi 60 } 61 62 // End returns the end position of the current Part. 63 func (pi *PathIterator[_]) End() int { 64 return pi.end 65 } 66 67 // IsLast returns true if the current Part is the last one. 68 func (pi *PathIterator[_]) IsLast() bool { 69 return pi.end == len(pi.path) 70 } 71 72 // Left returns the left path of the current Part. 73 func (pi *PathIterator[_]) Left() string { 74 return pi.path[:pi.start] 75 } 76 77 // LeftPart returns the left path and current Part. 78 func (pi *PathIterator[_]) LeftPart() string { 79 return pi.path[:pi.end] 80 } 81 82 // Next iterates through the next Part of the path. 83 // It returns false if there's no more parts. 84 func (pi *PathIterator[_]) Next() bool { 85 pi.start = pi.end + 1 86 if pi.start >= len(pi.path) { 87 pi.end = pi.start 88 89 return false 90 } 91 92 pos := strings.IndexByte(pi.path[pi.start:], pi.pathSeparator) 93 if pos == -1 { 94 pi.end = len(pi.path) 95 } else { 96 pi.end = pi.start + pos 97 } 98 99 return true 100 } 101 102 // Part returns the current Part. 103 func (pi *PathIterator[_]) Part() string { 104 return pi.path[pi.start:pi.end] 105 } 106 107 // Path returns the path to iterate. 108 func (pi *PathIterator[_]) Path() string { 109 return pi.path 110 } 111 112 // ReplacePart replaces the current Part of the path with the new path. 113 // If the path iterator has been reset it returns true. 114 // It can be used in symbolic link replacement. 115 func (pi *PathIterator[_]) ReplacePart(newPath string) bool { 116 vfs := pi.vfs 117 oldPath := pi.path 118 119 if vfs.IsAbs(newPath) { 120 pi.path = vfs.Join(newPath, oldPath[pi.end:]) 121 } else { 122 pi.path = vfs.Join(oldPath[:pi.start], newPath, oldPath[pi.end:]) 123 } 124 125 // If the old path before the current part is different, the iterator must be reset. 126 if pi.start >= len(pi.path) || pi.path[:pi.start] != oldPath[:pi.start] { 127 pi.Reset() 128 129 return true 130 } 131 132 // restart from the part before the symbolic link part. 133 pi.end = pi.start - 1 134 135 return false 136 } 137 138 // Reset resets the iterator. 139 func (pi *PathIterator[_]) Reset() { 140 pi.end = pi.volumeNameLen 141 } 142 143 // Right returns the right path of the current Part. 144 func (pi *PathIterator[_]) Right() string { 145 return pi.path[pi.end:] 146 } 147 148 // RightPart returns the right path and the current Part. 149 func (pi *PathIterator[_]) RightPart() string { 150 return pi.path[pi.start:] 151 } 152 153 // Start returns the start position of the current Part. 154 func (pi *PathIterator[_]) Start() int { 155 return pi.start 156 } 157 158 // VolumeName returns leading volume name. 159 func (pi *PathIterator[_]) VolumeName() string { 160 return pi.path[:pi.volumeNameLen] 161 } 162 163 // VolumeNameLen returns length of the leading volume name on Windows. 164 // It returns 0 elsewhere. 165 func (pi *PathIterator[_]) VolumeNameLen() int { 166 return pi.volumeNameLen 167 }