1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
//go:build !darwin && !(aix || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris)
// TODO: add a "portable_dirent" build tag so that we can test this
// on non-Windows platforms
package fastwalk
import (
"io/fs"
"os"
"sort"
"sync"
"github.com/charlievieth/fastwalk/internal/fmtdirent"
)
var _ DirEntry = (*portableDirent)(nil)
type portableDirent struct {
fs.DirEntry
parent string
stat *fileInfo
depth uint32
}
func (d *portableDirent) String() string {
return fmtdirent.FormatDirEntry(d)
}
func (d *portableDirent) Depth() int {
return int(d.depth)
}
func (d *portableDirent) Stat() (fs.FileInfo, error) {
if d.DirEntry.Type()&os.ModeSymlink == 0 {
return d.DirEntry.Info()
}
stat := loadFileInfo(&d.stat)
stat.once.Do(func() {
stat.FileInfo, stat.err = os.Stat(d.parent + string(os.PathSeparator) + d.Name())
})
return stat.FileInfo, stat.err
}
func newDirEntry(dirName string, info fs.DirEntry, depth int) DirEntry {
return &portableDirent{
DirEntry: info,
parent: dirName,
depth: uint32(depth),
}
}
func fileInfoToDirEntry(dirname string, fi fs.FileInfo) DirEntry {
return newDirEntry(dirname, fs.FileInfoToDirEntry(fi), 0)
}
var direntSlicePool = sync.Pool{
New: func() any {
a := make([]DirEntry, 0, 32)
return &a
},
}
func putDirentSlice(p *[]DirEntry) {
// max is half as many as Unix because twice the size
if p != nil && cap(*p) <= 16*1024 {
a := *p
for i := range a {
a[i] = nil
}
*p = a[:0]
direntSlicePool.Put(p)
}
}
func sortDirents(mode SortMode, dents []DirEntry) {
if len(dents) <= 1 {
return
}
switch mode {
case SortLexical:
sort.Slice(dents, func(i, j int) bool {
return dents[i].Name() < dents[j].Name()
})
case SortFilesFirst:
sort.Slice(dents, func(i, j int) bool {
d1 := dents[i]
d2 := dents[j]
r1 := d1.Type().IsRegular()
r2 := d2.Type().IsRegular()
switch {
case r1 && !r2:
return true
case !r1 && r2:
return false
case !r1 && !r2:
// Both are not regular files: sort directories last
dd1 := d1.Type().IsDir()
dd2 := d2.Type().IsDir()
switch {
case !dd1 && dd2:
return true
case dd1 && !dd2:
return false
}
}
return d1.Name() < d2.Name()
})
case SortDirsFirst:
sort.Slice(dents, func(i, j int) bool {
d1 := dents[i]
d2 := dents[j]
dd1 := d1.Type().IsDir()
dd2 := d2.Type().IsDir()
switch {
case dd1 && !dd2:
return true
case !dd1 && dd2:
return false
case !dd1 && !dd2:
// Both are not directories: sort regular files first
r1 := d1.Type().IsRegular()
r2 := d2.Type().IsRegular()
switch {
case r1 && !r2:
return true
case !r1 && r2:
return false
}
}
return d1.Name() < d2.Name()
})
}
}
|