Skip to content

Commit b250a01

Browse files
authored
Merge pull request #18 from ajnavarro/subdir
subdir: add subdir filesystem implementation
2 parents dc2c24b + 73de279 commit b250a01

File tree

4 files changed

+261
-0
lines changed

4 files changed

+261
-0
lines changed

subdir/file.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package subdir
2+
3+
import (
4+
"io"
5+
"path/filepath"
6+
"strings"
7+
8+
"gopkg.in/src-d/go-billy.v2"
9+
)
10+
11+
type file struct {
12+
billy.BaseFile
13+
14+
f billy.File
15+
}
16+
17+
func newFile(f billy.File, filename string) billy.File {
18+
return &file{
19+
BaseFile: billy.BaseFile{BaseFilename: resolve(filename)},
20+
f: f,
21+
}
22+
}
23+
24+
func (f *file) Read(p []byte) (int, error) {
25+
return f.f.Read(p)
26+
}
27+
28+
func (f *file) ReadAt(b []byte, off int64) (int, error) {
29+
rf, ok := f.f.(io.ReaderAt)
30+
if !ok {
31+
return 0, billy.ErrNotSupported
32+
}
33+
34+
return rf.ReadAt(b, off)
35+
}
36+
37+
func (f *file) Seek(offset int64, whence int) (int64, error) {
38+
return f.f.Seek(offset, whence)
39+
}
40+
41+
func (f *file) Write(p []byte) (int, error) {
42+
return f.f.Write(p)
43+
}
44+
45+
func (f *file) Close() error {
46+
defer func() { f.Closed = true }()
47+
return f.f.Close()
48+
}
49+
50+
func resolve(path string) string {
51+
rp := filepath.Clean(path)
52+
if rp == "/" {
53+
rp = "."
54+
} else if strings.HasPrefix(rp, "/") {
55+
rp = rp[1:]
56+
}
57+
58+
return rp
59+
}

subdir/fileinfo.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package subdir
2+
3+
import (
4+
"os"
5+
"time"
6+
7+
"gopkg.in/src-d/go-billy.v2"
8+
)
9+
10+
type fileInfo struct {
11+
filename string
12+
fi billy.FileInfo
13+
}
14+
15+
func newFileInfo(filename string, fi billy.FileInfo) billy.FileInfo {
16+
return &fileInfo{filename, fi}
17+
}
18+
19+
func (fi *fileInfo) Name() string {
20+
return fi.filename
21+
}
22+
func (fi *fileInfo) Size() int64 {
23+
return fi.fi.Size()
24+
}
25+
26+
func (fi *fileInfo) Mode() os.FileMode {
27+
return fi.fi.Mode()
28+
}
29+
30+
func (fi *fileInfo) ModTime() time.Time {
31+
return fi.fi.ModTime()
32+
}
33+
34+
func (fi *fileInfo) IsDir() bool {
35+
return fi.fi.IsDir()
36+
}
37+
38+
func (fi *fileInfo) Sys() interface{} {
39+
return fi.fi.Sys()
40+
}

subdir/subdir.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package subdir
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
8+
"gopkg.in/src-d/go-billy.v2"
9+
)
10+
11+
type subdirFs struct {
12+
underlying billy.Filesystem
13+
base string
14+
}
15+
16+
// New creates a new filesystem wrapping up the given 'fs'.
17+
// The created filesystem has its base in the given subdirectory
18+
// of the underlying filesystem.
19+
//
20+
// This is particularly useful to implement the Dir method for
21+
// other filesystems.
22+
func New(fs billy.Filesystem, base string) billy.Filesystem {
23+
return &subdirFs{fs, base}
24+
}
25+
26+
func (s *subdirFs) underlyingPath(filename string) string {
27+
return s.Join(s.Base(), filename)
28+
}
29+
30+
func (s *subdirFs) Create(filename string) (billy.File, error) {
31+
f, err := s.underlying.Create(s.underlyingPath(filename))
32+
if err != nil {
33+
return nil, err
34+
}
35+
36+
return newFile(f, filename), nil
37+
}
38+
39+
func (s *subdirFs) Open(filename string) (billy.File, error) {
40+
f, err := s.underlying.Open(s.underlyingPath(filename))
41+
if err != nil {
42+
return nil, err
43+
}
44+
45+
return newFile(f, filename), nil
46+
}
47+
48+
func (s *subdirFs) OpenFile(filename string, flag int, mode os.FileMode) (
49+
billy.File, error) {
50+
51+
f, err := s.underlying.OpenFile(s.underlyingPath(filename), flag, mode)
52+
if err != nil {
53+
return nil, err
54+
}
55+
56+
return newFile(f, filename), nil
57+
}
58+
59+
func (s *subdirFs) TempFile(dir, prefix string) (billy.File, error) {
60+
f, err := s.underlying.TempFile(s.underlyingPath(dir), prefix)
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
return newFile(f, s.Join(dir, filepath.Base(f.Filename()))), nil
66+
}
67+
68+
func (s *subdirFs) Rename(from, to string) error {
69+
return s.underlying.Rename(s.underlyingPath(from), s.underlyingPath(to))
70+
}
71+
72+
func (s *subdirFs) Remove(path string) error {
73+
return s.underlying.Remove(s.underlyingPath(path))
74+
}
75+
76+
func (s *subdirFs) MkdirAll(filename string, perm os.FileMode) error {
77+
fullpath := s.Join(s.base, filename)
78+
return s.underlying.MkdirAll(fullpath, perm)
79+
}
80+
81+
func (s *subdirFs) Stat(filename string) (billy.FileInfo, error) {
82+
filename = removeLeadingSlash(filename)
83+
fi, err := s.underlying.Stat(s.underlyingPath(filename))
84+
if err != nil {
85+
return nil, err
86+
}
87+
88+
return newFileInfo(filename, fi), nil
89+
}
90+
91+
func (s *subdirFs) ReadDir(path string) ([]billy.FileInfo, error) {
92+
prefix := s.underlyingPath(path)
93+
fis, err := s.underlying.ReadDir(prefix)
94+
if err != nil {
95+
return nil, err
96+
}
97+
for i := 0; i < len(fis); i++ {
98+
rn := strings.Replace(fis[i].Name(), prefix, "", 1)
99+
fis[i] = newFileInfo(rn, fis[i])
100+
}
101+
102+
return fis, nil
103+
}
104+
105+
func (s *subdirFs) Join(elem ...string) string {
106+
return s.underlying.Join(elem...)
107+
}
108+
109+
func (s *subdirFs) Dir(path string) billy.Filesystem {
110+
return New(s, removeLeadingSlash(path))
111+
}
112+
113+
func (s *subdirFs) Base() string {
114+
return s.base
115+
}
116+
117+
func removeLeadingSlash(path string) string {
118+
if strings.HasPrefix(path, "/") {
119+
return path[1:]
120+
}
121+
122+
return path
123+
}

subdir/suite_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package subdir
2+
3+
import (
4+
"io/ioutil"
5+
stdos "os"
6+
"testing"
7+
8+
"gopkg.in/src-d/go-billy.v2"
9+
"gopkg.in/src-d/go-billy.v2/osfs"
10+
"gopkg.in/src-d/go-billy.v2/test"
11+
12+
. "gopkg.in/check.v1"
13+
)
14+
15+
func Test(t *testing.T) { TestingT(t) }
16+
17+
type FilesystemSuite struct {
18+
test.FilesystemSuite
19+
cfs billy.Filesystem
20+
path string
21+
}
22+
23+
var _ = Suite(&FilesystemSuite{})
24+
25+
func (s *FilesystemSuite) SetUpTest(c *C) {
26+
s.path, _ = ioutil.TempDir(stdos.TempDir(), "go-git-fs-test")
27+
osFs := osfs.New(s.path)
28+
s.cfs = New(osFs, "test-subdir")
29+
s.FilesystemSuite.Fs = s.cfs
30+
}
31+
32+
func (s *FilesystemSuite) TearDownTest(c *C) {
33+
fi, err := ioutil.ReadDir(s.path)
34+
c.Assert(err, IsNil)
35+
c.Assert(len(fi) <= 1, Equals, true)
36+
37+
err = stdos.RemoveAll(s.path)
38+
c.Assert(err, IsNil)
39+
}

0 commit comments

Comments
 (0)