2023-01-27 15:04:58 +00:00
|
|
|
package fileset
|
|
|
|
|
|
|
|
import (
|
2023-01-31 17:34:36 +00:00
|
|
|
"fmt"
|
2023-01-27 15:04:58 +00:00
|
|
|
"io/fs"
|
2023-02-15 16:02:54 +00:00
|
|
|
"os"
|
2023-01-27 15:04:58 +00:00
|
|
|
"path/filepath"
|
|
|
|
)
|
|
|
|
|
|
|
|
// FileSet facilitates fast recursive file listing of a path.
|
|
|
|
// It optionally takes into account ignore rules through the [Ignorer] interface.
|
|
|
|
type FileSet struct {
|
|
|
|
root string
|
|
|
|
ignore Ignorer
|
|
|
|
}
|
|
|
|
|
|
|
|
// New returns a [FileSet] for the given root path.
|
|
|
|
func New(root string) *FileSet {
|
|
|
|
return &FileSet{
|
|
|
|
root: filepath.Clean(root),
|
|
|
|
ignore: nopIgnorer{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ignorer returns the [FileSet]'s current ignorer.
|
|
|
|
func (w *FileSet) Ignorer() Ignorer {
|
|
|
|
return w.ignore
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetIgnorer sets the [Ignorer] interface for this [FileSet].
|
|
|
|
func (w *FileSet) SetIgnorer(ignore Ignorer) {
|
|
|
|
w.ignore = ignore
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return root for fileset.
|
|
|
|
func (w *FileSet) Root() string {
|
|
|
|
return w.root
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return all tracked files for Repo
|
|
|
|
func (w *FileSet) All() ([]File, error) {
|
2023-08-21 07:35:02 +00:00
|
|
|
return w.recursiveListFiles()
|
2023-01-27 15:04:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Recursively traverses dir in a depth first manner and returns a list of all files
|
|
|
|
// that are being tracked in the FileSet (ie not being ignored for matching one of the
|
|
|
|
// patterns in w.ignore)
|
2023-08-21 07:35:02 +00:00
|
|
|
func (w *FileSet) recursiveListFiles() (fileList []File, err error) {
|
|
|
|
err = filepath.WalkDir(w.root, func(path string, d fs.DirEntry, err error) error {
|
2023-01-27 15:04:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
relPath, err := filepath.Rel(w.root, path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-02-15 16:02:54 +00:00
|
|
|
// skip symlinks
|
|
|
|
info, err := d.Info()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if info.Mode()&os.ModeSymlink != 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-01-27 15:04:58 +00:00
|
|
|
if d.IsDir() {
|
2023-01-31 17:34:36 +00:00
|
|
|
ign, err := w.ignore.IgnoreDirectory(relPath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("cannot check if %s should be ignored: %w", relPath, err)
|
|
|
|
}
|
|
|
|
if ign {
|
2023-01-27 15:04:58 +00:00
|
|
|
return filepath.SkipDir
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-01-31 17:34:36 +00:00
|
|
|
ign, err := w.ignore.IgnoreFile(relPath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("cannot check if %s should be ignored: %w", relPath, err)
|
|
|
|
}
|
|
|
|
if ign {
|
2023-01-27 15:04:58 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
fileList = append(fileList, File{d, path, relPath})
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|