github.com/haraldrudell/parl@v0.4.176/pfs/dir-iterator.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package pfs
     7  
     8  import (
     9  	"github.com/haraldrudell/parl"
    10  	"github.com/haraldrudell/parl/iters"
    11  )
    12  
    13  // DirIterator traverses file-system directories
    14  type DirIterator struct {
    15  	// traverser is the file-system traverser.
    16  	// Only method is [Traverser.Next]
    17  	traverser Traverser
    18  	// BaseIterator provides iterator methods:
    19  	// [iters.Iterator.Cancel] [iters.Iterator.Cond] [iters.Iterator.Next]
    20  	iters.BaseIterator[ResultEntry]
    21  }
    22  
    23  // NewIterator returns an iterator for a file-system entry and any child entries if directory
    24  //   - path is the initial path for the file-system walk.
    25  //     it may be relative or absolute, contain symlinks and
    26  //     point to a file, directory or special file
    27  //   - only directories are returned.
    28  //     if directories are not skipped, they descended into.
    29  //   - symlinks are followed.
    30  //     Broken symlinks are ignored.
    31  //   - any errored file-system entry cancels the iterator with error.
    32  func NewDirIterator(path string) (iterator iters.Iterator[ResultEntry]) {
    33  	i := DirIterator{traverser: *NewTraverser(path)}
    34  	i.BaseIterator = *iters.NewBaseIterator(i.iteratorAction)
    35  	return &i
    36  }
    37  
    38  // Init implements the right-hand side of a short variable declaration in
    39  // the init statement of a Go “for” clause
    40  //
    41  //		for i, iterator := iters.NewSlicePointerIterator(someSlice).Init(); iterator.Cond(&i); {
    42  //	   // i is pointer to slice element
    43  func (i *DirIterator) Init() (result ResultEntry, iterator iters.Iterator[ResultEntry]) {
    44  	iterator = i
    45  	return
    46  }
    47  
    48  // iteratorAction provides items to the BaseIterator
    49  func (t *DirIterator) iteratorAction(isCancel bool) (result ResultEntry, err error) {
    50  	if isCancel {
    51  		return
    52  	}
    53  	for {
    54  		result = t.traverser.Next()
    55  
    56  		// handle end
    57  		if result.IsEnd() {
    58  			err = parl.ErrEndCallbacks
    59  			return
    60  		}
    61  
    62  		// ignore broken symlink
    63  		if result.Reason == RSymlinkBad {
    64  			continue
    65  		}
    66  
    67  		// any other error cancels iterator
    68  		if result.Err != nil {
    69  			err = result.Err
    70  			return
    71  		}
    72  
    73  		//	- ignore any file-system entry that is not
    74  		//		a directory
    75  		if !result.IsDir() {
    76  			continue
    77  		}
    78  
    79  		return // directory return
    80  	}
    81  }