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  }