dirent.go

 1package fastwalk
 2
 3import (
 4	"io/fs"
 5	"os"
 6	"sync"
 7	"sync/atomic"
 8	"syscall"
 9	"unsafe"
10)
11
12type fileInfo struct {
13	once sync.Once
14	fs.FileInfo
15	err error
16}
17
18func loadFileInfo(pinfo **fileInfo) *fileInfo {
19	ptr := (*unsafe.Pointer)(unsafe.Pointer(pinfo))
20	fi := (*fileInfo)(atomic.LoadPointer(ptr))
21	if fi == nil {
22		fi = &fileInfo{}
23		if !atomic.CompareAndSwapPointer(
24			(*unsafe.Pointer)(unsafe.Pointer(pinfo)), // adrr
25			nil,                                      // old
26			unsafe.Pointer(fi),                       // new
27		) {
28			fi = (*fileInfo)(atomic.LoadPointer(ptr))
29		}
30	}
31	return fi
32}
33
34// StatDirEntry returns a [fs.FileInfo] describing the named file ([os.Stat]).
35// If de is a [fastwalk.DirEntry] its Stat method is used and the returned
36// FileInfo may be cached from a prior call to Stat. If a cached result is not
37// desired, users should just call [os.Stat] directly.
38//
39// This is a helper function for calling Stat on the DirEntry passed to the
40// walkFn argument to [Walk].
41//
42// The path argument is only used if de is not of type [fastwalk.DirEntry].
43// Therefore, de should be the DirEntry describing path.
44func StatDirEntry(path string, de fs.DirEntry) (fs.FileInfo, error) {
45	if de == nil {
46		return nil, &os.PathError{Op: "stat", Path: path, Err: syscall.EINVAL}
47	}
48	if de.Type()&os.ModeSymlink == 0 {
49		return de.Info()
50	}
51	if d, ok := de.(DirEntry); ok {
52		return d.Stat()
53	}
54	return os.Stat(path)
55}
56
57// DirEntryDepth returns the depth at which entry de was generated relative
58// to the root being walked or -1 if de does not have type [fastwalk.DirEntry].
59//
60// This is a helper function that saves the user from having to cast the
61// [fs.DirEntry] argument to their walk function to a [fastwalk.DirEntry]
62// and is equivalent to the below code:
63//
64//	if d, _ := de.(DirEntry); d != nil {
65//		return d.Depth()
66//	}
67//	return -1
68func DirEntryDepth(de fs.DirEntry) int {
69	if d, _ := de.(DirEntry); d != nil {
70		return d.Depth()
71	}
72	return -1
73}