Skip to content

Commit

Permalink
add RemoveAll
Browse files Browse the repository at this point in the history
RemoveAll delegates to filesystem's RemoveAll if it is implemented.
Otherwise, it falls back to a generic implementation.

Semantics are the same as os.RemoveAll.
  • Loading branch information
smola committed Feb 14, 2017
1 parent dd02181 commit 729845e
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 0 deletions.
77 changes: 77 additions & 0 deletions fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,80 @@ func (f *BaseFile) Filename() string {
func (f *BaseFile) IsClosed() bool {
return f.Closed
}

type removerAll interface {
RemoveAll(string) error
}

// RemoveAll removes path and any children it contains.
// It removes everything it can but returns the first error
// it encounters. If the path does not exist, RemoveAll
// returns nil (no error).
func RemoveAll(fs Filesystem, path string) error {
r, ok := fs.(removerAll)
if ok {
return r.RemoveAll(path)
}

return removeAll(fs, path)
}

func removeAll(fs Filesystem, path string) error {
// This implementation is adapted from os.RemoveAll.

// Simple case: if Remove works, we're done.
err := fs.Remove(path)
if err == nil || os.IsNotExist(err) {
return nil
}

// Otherwise, is this a directory we need to recurse into?
dir, serr := fs.Stat(path)
if serr != nil {
if os.IsNotExist(serr) {
return nil
}

return serr
}

if !dir.IsDir() {
// Not a directory; return the error from Remove.
return err
}

// Directory.
fis, err := fs.ReadDir(path)
if err != nil {
if os.IsNotExist(err) {
// Race. It was deleted between the Lstat and Open.
// Return nil per RemoveAll's docs.
return nil
}

return err
}

// Remove contents & return first error.
err = nil
for _, fi := range fis {
cpath := fs.Join(path, fi.Name())
err1 := removeAll(fs, cpath)
if err == nil {
err = err1
}
}

// Remove directory.
err1 := fs.Remove(path)
if err1 == nil || os.IsNotExist(err1) {
return nil
}

if err == nil {
err = err1
}

return err

}
7 changes: 7 additions & 0 deletions osfs/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ func (fs *OS) Base() string {
return fs.base
}

// RemoveAll removes a file or directory recursively. Removes everything it can,
// but returns the first error.
func (fs *OS) RemoveAll(path string) error {
fullpath := fs.Join(fs.base, path)
return os.RemoveAll(fullpath)
}

// osFile represents a file in the os filesystem
type osFile struct {
billy.BaseFile
Expand Down
31 changes: 31 additions & 0 deletions test/fs_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,3 +512,34 @@ func (s *FilesystemSuite) TestReadWriteLargeFile(c *C) {
c.Assert(err, IsNil)
c.Assert(len(b), Equals, size)
}

func (s *FilesystemSuite) TestRemoveAllNonExistent(c *C) {
c.Assert(RemoveAll(s.Fs, "non-existent"), IsNil)
}

func (s *FilesystemSuite) TestRemoveAll(c *C) {
fnames := []string{
"foo/1",
"foo/2",
"foo/bar/1",
"foo/bar/2",
"foo/bar/baz/1",
"foo/bar/baz/qux/1",
"foo/bar/baz/qux/2",
"foo/bar/baz/qux/3",
}

for _, fname := range fnames {
f, err := s.Fs.Create(fname)
c.Assert(err, IsNil)
c.Assert(f.Close(), IsNil)
}

c.Assert(RemoveAll(s.Fs, "foo"), IsNil)

for _, fname := range fnames {
_, err := s.Fs.Stat(fname)
comment := Commentf("not removed: %s %s", fname, err)
c.Assert(os.IsNotExist(err), Equals, true, comment)
}
}

0 comments on commit 729845e

Please sign in to comment.