forked from mirror/afero
add Walk function similar to filepath.Walk, add test for Walk
This commit is contained in:
parent
582dcbe54c
commit
56c149609a
82
fs.go
82
fs.go
|
@ -26,6 +26,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -119,6 +120,87 @@ func ReadDir(dirname string, fs Fs) ([]os.FileInfo, error) {
|
||||||
return list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readDirNames reads the directory named by dirname and returns
|
||||||
|
// a sorted list of directory entries.
|
||||||
|
// adapted from https://golang.org/src/path/filepath/path.go
|
||||||
|
func readDirNames(dirname string, fs Fs) ([]string, error) {
|
||||||
|
f, err := fs.Open(dirname)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
names, err := f.Readdirnames(-1)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// walk recursively descends path, calling walkFn
|
||||||
|
// adapted from https://golang.org/src/path/filepath/path.go
|
||||||
|
func walk(path string, info os.FileInfo, walkFn filepath.WalkFunc, fs Fs) error {
|
||||||
|
err := walkFn(path, info, nil)
|
||||||
|
if err != nil {
|
||||||
|
if info.IsDir() && err == filepath.SkipDir {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
names, err := readDirNames(path, fs)
|
||||||
|
if err != nil {
|
||||||
|
return walkFn(path, info, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
filename := filepath.Join(path, name)
|
||||||
|
fileInfo, err := lstatIfOs(filename, fs)
|
||||||
|
if err != nil {
|
||||||
|
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = walk(filename, fileInfo, walkFn, fs)
|
||||||
|
if err != nil {
|
||||||
|
if !fileInfo.IsDir() || err != filepath.SkipDir {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the filesystem is OsFs use Lstat, else use fs.Stat
|
||||||
|
func lstatIfOs(path string, fs Fs) (info os.FileInfo, err error) {
|
||||||
|
_, ok := fs.(*OsFs)
|
||||||
|
if ok {
|
||||||
|
info, err = os.Lstat(path)
|
||||||
|
} else {
|
||||||
|
info, err = fs.Stat(path)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk walks the file tree rooted at root, calling walkFn for each file or
|
||||||
|
// directory in the tree, including root. All errors that arise visiting files
|
||||||
|
// and directories are filtered by walkFn. The files are walked in lexical
|
||||||
|
// order, which makes the output deterministic but means that for very
|
||||||
|
// large directories Walk can be inefficient.
|
||||||
|
// Walk does not follow symbolic links.
|
||||||
|
func Walk(root string, walkFn filepath.WalkFunc, fs Fs) error {
|
||||||
|
info, err := lstatIfOs(root, fs)
|
||||||
|
if err != nil {
|
||||||
|
return walkFn(root, nil, err)
|
||||||
|
}
|
||||||
|
return walk(root, info, walkFn, fs)
|
||||||
|
}
|
||||||
|
|
||||||
// byName implements sort.Interface.
|
// byName implements sort.Interface.
|
||||||
type byName []os.FileInfo
|
type byName []os.FileInfo
|
||||||
|
|
||||||
|
|
44
fs_test.go
44
fs_test.go
|
@ -28,14 +28,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var dot = []string{
|
|
||||||
"fs.go",
|
|
||||||
"fs_test.go",
|
|
||||||
"httpFs.go",
|
|
||||||
"memfile.go",
|
|
||||||
"memmap.go",
|
|
||||||
}
|
|
||||||
|
|
||||||
var testDir = "/tmp/afero"
|
var testDir = "/tmp/afero"
|
||||||
var testSubDir = "/tmp/afero/we/have/to/go/deeper"
|
var testSubDir = "/tmp/afero/we/have/to/go/deeper"
|
||||||
var testName = "test.txt"
|
var testName = "test.txt"
|
||||||
|
@ -450,6 +442,42 @@ func (m myFileInfo) String() string {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWalk(t *testing.T) {
|
||||||
|
outputs := make([]string, len(Fss))
|
||||||
|
for i, fs := range Fss {
|
||||||
|
walkFn := func(path string, info os.FileInfo, err error) error {
|
||||||
|
var size int64
|
||||||
|
if !info.IsDir() {
|
||||||
|
size = info.Size()
|
||||||
|
}
|
||||||
|
outputs[i] += fmt.Sprintln(path, info.Name(), size, info.IsDir(), err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := Walk(testDir, walkFn, fs)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail := false
|
||||||
|
for i, o := range outputs {
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if o != outputs[i-1] {
|
||||||
|
fail = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fail {
|
||||||
|
t.Log("Walk outputs not equal!")
|
||||||
|
for i, o := range outputs {
|
||||||
|
t.Log(Fss[i].Name())
|
||||||
|
t.Log(o)
|
||||||
|
}
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestReaddirAll(t *testing.T) {
|
func TestReaddirAll(t *testing.T) {
|
||||||
defer removeTestDir(t)
|
defer removeTestDir(t)
|
||||||
for _, fs := range Fss {
|
for _, fs := range Fss {
|
||||||
|
|
Loading…
Reference in New Issue