...

Source file src/github.com/dsoprea/go-utility/v2/filesystem/list_files.go

Documentation: github.com/dsoprea/go-utility/v2/filesystem

     1  package rifs
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  	"path"
     7  
     8  	"github.com/dsoprea/go-logging"
     9  )
    10  
    11  // FileListFilterPredicate is the callback predicate used for filtering.
    12  type FileListFilterPredicate func(parent string, child os.FileInfo) (hit bool, err error)
    13  
    14  // VisitedFile is one visited file.
    15  type VisitedFile struct {
    16  	Filepath string
    17  	Info     os.FileInfo
    18  	Index    int
    19  }
    20  
    21  // ListFiles feeds a continuous list of files from a recursive folder scan. An
    22  // optional predicate can be provided in order to filter. When done, the
    23  // `filesC` channel is closed. If there's an error, the `errC` channel will
    24  // receive it.
    25  func ListFiles(rootPath string, cb FileListFilterPredicate) (filesC chan VisitedFile, count int, errC chan error) {
    26  	defer func() {
    27  		if state := recover(); state != nil {
    28  			err := log.Wrap(state.(error))
    29  			log.Panic(err)
    30  		}
    31  	}()
    32  
    33  	// Make sure the path exists.
    34  
    35  	f, err := os.Open(rootPath)
    36  	log.PanicIf(err)
    37  
    38  	f.Close()
    39  
    40  	// Do our thing.
    41  
    42  	filesC = make(chan VisitedFile, 100)
    43  	errC = make(chan error, 1)
    44  	index := 0
    45  
    46  	go func() {
    47  		defer func() {
    48  			if state := recover(); state != nil {
    49  				err := log.Wrap(state.(error))
    50  				errC <- err
    51  			}
    52  		}()
    53  
    54  		queue := []string{rootPath}
    55  		for len(queue) > 0 {
    56  			// Pop the next folder to process off the queue.
    57  			var thisPath string
    58  			thisPath, queue = queue[0], queue[1:]
    59  
    60  			// Skip path if a symlink.
    61  
    62  			fi, err := os.Lstat(thisPath)
    63  			log.PanicIf(err)
    64  
    65  			if (fi.Mode() & os.ModeSymlink) > 0 {
    66  				continue
    67  			}
    68  
    69  			// Read information.
    70  
    71  			folderF, err := os.Open(thisPath)
    72  			if err != nil {
    73  				errC <- log.Wrap(err)
    74  				return
    75  			}
    76  
    77  			// Iterate through children.
    78  
    79  			for {
    80  				children, err := folderF.Readdir(1000)
    81  				if err == io.EOF {
    82  					break
    83  				} else if err != nil {
    84  					errC <- log.Wrap(err)
    85  					return
    86  				}
    87  
    88  				for _, child := range children {
    89  					filepath := path.Join(thisPath, child.Name())
    90  
    91  					// Skip if a file symlink.
    92  
    93  					fi, err := os.Lstat(filepath)
    94  					log.PanicIf(err)
    95  
    96  					if (fi.Mode() & os.ModeSymlink) > 0 {
    97  						continue
    98  					}
    99  
   100  					// If a predicate was given, determine if this child will be
   101  					// left behind.
   102  					if cb != nil {
   103  						hit, err := cb(thisPath, child)
   104  
   105  						if err != nil {
   106  							errC <- log.Wrap(err)
   107  							return
   108  						}
   109  
   110  						if hit == false {
   111  							continue
   112  						}
   113  					}
   114  
   115  					index++
   116  
   117  					// Push file to channel.
   118  
   119  					vf := VisitedFile{
   120  						Filepath: filepath,
   121  						Info:     child,
   122  						Index:    index,
   123  					}
   124  
   125  					filesC <- vf
   126  
   127  					// If a folder, queue for later processing.
   128  
   129  					if child.IsDir() == true {
   130  						queue = append(queue, filepath)
   131  					}
   132  				}
   133  			}
   134  
   135  			folderF.Close()
   136  		}
   137  
   138  		close(filesC)
   139  		close(errC)
   140  	}()
   141  
   142  	return filesC, index, errC
   143  }
   144  

View as plain text