Skip to content

Commit 8209194

Browse files
committed
add MkdirAll
1 parent 9c1722d commit 8209194

File tree

4 files changed

+112
-8
lines changed

4 files changed

+112
-8
lines changed

fs.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var (
2121
// * Get a temporal file.
2222
// * Rename files.
2323
// * Remove files.
24+
// * Create directories.
2425
// * Join parts of path.
2526
// * Obtain a filesystem starting on a subdirectory in the current filesystem.
2627
// * Get the base path for the filesystem.
@@ -35,6 +36,7 @@ type Filesystem interface {
3536
TempFile(dir, prefix string) (File, error)
3637
Rename(from, to string) error
3738
Remove(filename string) error
39+
MkdirAll(filename string, perm os.FileMode) error
3840
Join(elem ...string) string
3941
Dir(path string) Filesystem
4042
Base() string

memfs/memory.go

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,19 @@ func (fs *Memory) OpenFile(filename string, flag int, perm os.FileMode) (billy.F
4646
fullpath := fs.Join(fs.base, filename)
4747
f, ok := fs.s.files[fullpath]
4848

49-
if !ok && !isCreate(flag) {
50-
return nil, os.ErrNotExist
51-
}
49+
if !ok {
50+
if !isCreate(flag) {
51+
return nil, os.ErrNotExist
52+
}
5253

53-
if f == nil {
5454
fs.s.files[fullpath] = newFile(fs.base, fullpath, flag)
5555
return fs.s.files[fullpath], nil
5656
}
5757

58+
if f.isDir {
59+
return nil, fmt.Errorf("cannot open directory: %s", filename)
60+
}
61+
5862
n := newFile(fs.base, fullpath, flag)
5963
n.content = f.content
6064

@@ -73,12 +77,13 @@ func (fs *Memory) OpenFile(filename string, flag int, perm os.FileMode) (billy.F
7377
func (fs *Memory) Stat(filename string) (billy.FileInfo, error) {
7478
fullpath := fs.Join(fs.base, filename)
7579

76-
if _, ok := fs.s.files[fullpath]; ok {
80+
f, ok := fs.s.files[fullpath]
81+
if ok && !f.isDir {
7782
return newFileInfo(fs.base, fullpath, fs.s.files[fullpath].content.Len()), nil
7883
}
7984

8085
info, err := fs.ReadDir(fullpath)
81-
if err == nil && len(info) != 0 {
86+
if err == nil && len(info) != 0 || f != nil && f.isDir {
8287
fi := newFileInfo(fs.base, fullpath, len(info))
8388
fi.isDir = true
8489
return fi, nil
@@ -101,6 +106,10 @@ func (fs *Memory) ReadDir(base string) (entries []billy.FileInfo, err error) {
101106
parts := strings.Split(fullpath, string(separator))
102107

103108
if len(parts) == 1 {
109+
if f.isDir {
110+
entries = append(entries, &fileInfo{name: parts[0], isDir: true})
111+
}
112+
104113
entries = append(entries, &fileInfo{name: parts[0], size: f.content.Len()})
105114
continue
106115
}
@@ -116,6 +125,22 @@ func (fs *Memory) ReadDir(base string) (entries []billy.FileInfo, err error) {
116125
return
117126
}
118127

128+
// MkdirAll creates a directory.
129+
func (fs *Memory) MkdirAll(path string, perm os.FileMode) error {
130+
fullpath := fs.Join(fs.base, path)
131+
f, ok := fs.s.files[fullpath]
132+
if ok {
133+
if !f.isDir {
134+
return fmt.Errorf("%s is a file", path)
135+
}
136+
137+
return nil
138+
}
139+
140+
fs.s.files[fullpath] = &file{isDir: true}
141+
return nil
142+
}
143+
119144
var maxTempFiles = 1024 * 4
120145

121146
// TempFile creates a new temporary file.
@@ -207,6 +232,7 @@ type file struct {
207232
content *content
208233
position int64
209234
flag int
235+
isDir bool
210236
}
211237

212238
func newFile(base, fullpath string, flag int) *file {

osfs/os.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import (
1010
"srcd.works/go-billy.v1"
1111
)
1212

13+
const (
14+
defaultDirectoryMode = 0755
15+
defaultCreateMode = 0666
16+
)
17+
1318
// OS is a filesystem based on the os filesystem
1419
type OS struct {
1520
base string
@@ -25,7 +30,7 @@ func New(baseDir string) *OS {
2530
// Create creates a file and opens it with standard permissions
2631
// and modes O_RDWR, O_CREATE and O_TRUNC.
2732
func (fs *OS) Create(filename string) (billy.File, error) {
28-
return fs.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
33+
return fs.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, defaultCreateMode)
2934
}
3035

3136
// OpenFile is equivalent to standard os.OpenFile.
@@ -55,7 +60,7 @@ func (fs *OS) OpenFile(filename string, flag int, perm os.FileMode) (billy.File,
5560
func (fs *OS) createDir(fullpath string) error {
5661
dir := filepath.Dir(fullpath)
5762
if dir != "." {
58-
if err := os.MkdirAll(dir, 0755); err != nil {
63+
if err := os.MkdirAll(dir, defaultDirectoryMode); err != nil {
5964
return err
6065
}
6166
}
@@ -93,6 +98,12 @@ func (fs *OS) Rename(from, to string) error {
9398
return os.Rename(from, to)
9499
}
95100

101+
// MkdirAll creates a directory.
102+
func (fs *OS) MkdirAll(path string, perm os.FileMode) error {
103+
fullpath := fs.Join(fs.base, path)
104+
return os.MkdirAll(fullpath, defaultDirectoryMode)
105+
}
106+
96107
// Open opens a file in read-only mode.
97108
func (fs *OS) Open(filename string) (billy.File, error) {
98109
return fs.OpenFile(filename, os.O_RDONLY, 0)

test/fs_suite.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,14 @@ func (s *FilesystemSuite) TestRemoveAllNonExistent(c *C) {
516516
c.Assert(RemoveAll(s.Fs, "non-existent"), IsNil)
517517
}
518518

519+
func (s *FilesystemSuite) TestRemoveAllEmptyDir(c *C) {
520+
c.Assert(s.Fs.MkdirAll("empty", os.FileMode(0755)), IsNil)
521+
c.Assert(RemoveAll(s.Fs, "empty"), IsNil)
522+
_, err := s.Fs.Stat("empty")
523+
c.Assert(err, NotNil)
524+
c.Assert(os.IsNotExist(err), Equals, true)
525+
}
526+
519527
func (s *FilesystemSuite) TestRemoveAll(c *C) {
520528
fnames := []string{
521529
"foo/1",
@@ -569,3 +577,60 @@ func (s *FilesystemSuite) TestRemoveAllRelative(c *C) {
569577
c.Assert(os.IsNotExist(err), Equals, true, comment)
570578
}
571579
}
580+
581+
func (s *FilesystemSuite) TestMkdirAll(c *C) {
582+
err := s.Fs.MkdirAll("empty", os.FileMode(0755))
583+
c.Assert(err, IsNil)
584+
fi, err := s.Fs.Stat("empty")
585+
c.Assert(err, IsNil)
586+
c.Assert(fi.IsDir(), Equals, true)
587+
}
588+
589+
func (s *FilesystemSuite) TestMkdirAllNested(c *C) {
590+
err := s.Fs.MkdirAll("foo/bar/baz", os.FileMode(0755))
591+
c.Assert(err, IsNil)
592+
fi, err := s.Fs.Stat("foo/bar/baz")
593+
c.Assert(err, IsNil)
594+
c.Assert(fi.IsDir(), Equals, true)
595+
}
596+
597+
func (s *FilesystemSuite) TestMkdirAllIdempotent(c *C) {
598+
err := s.Fs.MkdirAll("empty", os.FileMode(0755))
599+
c.Assert(err, IsNil)
600+
fi, err := s.Fs.Stat("empty")
601+
c.Assert(err, IsNil)
602+
c.Assert(fi.IsDir(), Equals, true)
603+
604+
// idempotent
605+
err = s.Fs.MkdirAll("empty", os.FileMode(0755))
606+
c.Assert(err, IsNil)
607+
fi, err = s.Fs.Stat("empty")
608+
c.Assert(err, IsNil)
609+
c.Assert(fi.IsDir(), Equals, true)
610+
}
611+
612+
func (s *FilesystemSuite) TestMkdirAllAndOpenFile(c *C) {
613+
err := s.Fs.MkdirAll("dir", os.FileMode(0755))
614+
c.Assert(err, IsNil)
615+
616+
f, err := s.Fs.Create("dir/bar/foo")
617+
c.Assert(err, IsNil)
618+
c.Assert(f.Close(), IsNil)
619+
620+
fi, err := s.Fs.Stat("dir/bar/foo")
621+
c.Assert(err, IsNil)
622+
c.Assert(fi.IsDir(), Equals, false)
623+
}
624+
625+
func (s *FilesystemSuite) TestMkdirAllWithExistingFile(c *C) {
626+
f, err := s.Fs.Create("dir/foo")
627+
c.Assert(err, IsNil)
628+
c.Assert(f.Close(), IsNil)
629+
630+
err = s.Fs.MkdirAll("dir/foo", os.FileMode(0755))
631+
c.Assert(err, NotNil)
632+
633+
fi, err := s.Fs.Stat("dir/foo")
634+
c.Assert(err, IsNil)
635+
c.Assert(fi.IsDir(), Equals, false)
636+
}

0 commit comments

Comments
 (0)