Skip to content

Commit

Permalink
fs/fsutil: Extend Control with IntersectInclude member function
Browse files Browse the repository at this point in the history
  • Loading branch information
rjeczalik committed Aug 10, 2014
1 parent 678c49b commit c5ee714
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 16 deletions.
64 changes: 57 additions & 7 deletions fs/fsutil/fsutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,25 @@ package fsutil
import (
"os"
"path/filepath"
"sort"
"strings"

"github.com/rjeczalik/tools/fs"
"github.com/rjeczalik/tools/fs/memfs"
)

// Readpaths reads paths of all the files and directories of the 'dir' directory.
// If none files were found, the 'files' slice will be nil. If none directories
// were found, the 'dirs' slice will be nil. If the 'dir' was empty or error
// occured during accessing the filesystem, both slice will be empty.
func Readpaths(dir string) (files, dirs []string) {
return defaultControl.Readpaths(dir)
return Default.Readpaths(dir)
}

// Readdirpaths reads all paths of all subdirectories of the 'dir', except
// the ones which begin with a dot.
func Readdirpaths(dir string) []string {
return defaultControl.Readdirpaths(dir)
return Default.Readdirpaths(dir)
}

// Readdirpaths reads all names of all subdirectories of the 'dir', except
Expand Down Expand Up @@ -58,7 +60,7 @@ func Readdirnames(dir string) []string {
//
// []string{"github.com/user/example"}
func Intersect(src, dir string) []string {
return defaultControl.Intersect(src, dir)
return Default.Intersect(src, dir)
}

// Find globs into 'dir' directory, reading all files and directories except those,
Expand All @@ -69,7 +71,7 @@ func Intersect(src, dir string) []string {
//
// On success it returns full paths for files and directories it found.
func Find(dir string, n int) []string {
return defaultControl.Find(dir, n)
return Default.Find(dir, n)
}

// Control is the package control structure, allows for altering the behavior
Expand Down Expand Up @@ -148,7 +150,7 @@ func (c Control) readall(dir string) (files, dirs []string) {
return
}

func depthBelow(depth int, root, dir string) bool {
func isDepthBelow(depth int, root, dir string) bool {
if depth <= 0 {
return true
}
Expand Down Expand Up @@ -176,7 +178,7 @@ func (c Control) Find(dir string, n int) []string {
}
for _, d := range dirs {
d = filepath.Join(path, filepath.Base(d))
if depthBelow(n, dir, d) {
if isDepthBelow(n, dir, d) {
glob = append(glob, d)
}
all = append(all, d)
Expand Down Expand Up @@ -226,11 +228,59 @@ func (c Control) Intersect(src, dir string) []string {
return s
}

func notindirs(s []string, x string) bool {
for i := range s {
if len(s[i]) > len(x) && strings.HasSuffix(s[i], x) &&
s[i][len(s[i])-len(x)-1] == os.PathSeparator {
return false
}
}
return true
}

// IntersectInclude is not documented yet, please see TestIntersectInclude for
// temporary usage details.
//
// TODO(rjeczalik): document
func (c Control) IntersectInclude(src, dir string) map[string][]string {
var (
old = c.FS
spy = memfs.New()
tee = TeeFilesystem(old, spy)
)
c.FS = tee
dirs := c.Intersect(src, dir)
c.FS = old
switch len(dirs) {
case 0:
return nil
case 1:
return map[string][]string{dirs[0]: nil}
}
sort.StringSlice(dirs).Sort()
m := make(map[string][]string, len(dirs))
for i := 1; i < len(dirs); i++ {
m[dirs[i]] = nil
j, n := strings.Index(dirs[i], dirs[i-1]), len(dirs[i-1])
if j == -1 || dirs[i][j+n] != os.PathSeparator {
continue
}
for _, name := range (Control{FS: spy, Hidden: c.Hidden}).Readdirnames(
filepath.Join(dir, dirs[i-1])) {
if notindirs(dirs, name) {
m[dirs[i-1]] = append(m[dirs[i-1]], filepath.Join(dirs[i-1], name))
}
}
}
return m
}

func (c Control) hidden(name string) bool {
return !c.Hidden && name[0] == '.'
}

var defaultControl = Control{
// Default is not documented yet, altougth it really hopes to be.
var Default = Control{
FS: fs.Default,
Hidden: false,
}
108 changes: 99 additions & 9 deletions fs/fsutil/fsutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ LOOP:
return true
}

func equaldiff(lhs, cas map[string][]string) bool {
if len(lhs) != len(cas) {
return false
}
for k, cas := range cas {
if lhs, ok := lhs[filepath.FromSlash(k)]; !ok || !equal(lhs, cas) {
return false
}
}
return true
}

func TestReadpaths(t *testing.T) {
t.Skip("TODO(rjeczalik)")
}
Expand Down Expand Up @@ -175,13 +187,12 @@ func TestReaddirnames(t *testing.T) {

func TestCatchSpy(t *testing.T) {
cases := [...]struct {
c Control
depth int
c Control
dirs map[string][]string
}{
0: {
Control{FS: trees[3]},
1,
1, Control{FS: trees[3]},
map[string][]string{
filepath.FromSlash("/"): {
"/1",
Expand All @@ -202,8 +213,7 @@ func TestCatchSpy(t *testing.T) {
},
},
1: {
Control{FS: trees[3], Hidden: true},
1,
1, Control{FS: trees[3], Hidden: true},
map[string][]string{
filepath.FromSlash("/"): {
"/.1",
Expand Down Expand Up @@ -233,8 +243,7 @@ func TestCatchSpy(t *testing.T) {
},
},
2: {
Control{FS: trees[3]},
3,
3, Control{FS: trees[3]},
map[string][]string{
filepath.FromSlash("/abc/1"): {
"/abc/1/2",
Expand Down Expand Up @@ -283,7 +292,6 @@ func TestCatchSpy(t *testing.T) {
if !equal(found, v) {
t.Errorf("want found=%v; got %v (i=%d, dir=%s, j=%d)", v,
found, i, dir, j)
continue
}
}
found := (Control{FS: spy, Hidden: true}).Find(dir, cas.depth)
Expand All @@ -293,7 +301,6 @@ func TestCatchSpy(t *testing.T) {
}
if !equal(found, v) {
t.Errorf("want found=%v; got %v (i=%d, dir=%s)", v, found, i, dir)
continue
}
}
}
Expand Down Expand Up @@ -369,6 +376,89 @@ func TestIntersect(t *testing.T) {
}
}

func TestIntersectInclude(t *testing.T) {
cases := [...]struct {
c Control
diff map[string][]string
src string
dst string
}{
0: {
Control{FS: trees[1]},
map[string][]string{
"github.com/user/example/dir": nil,
"github.com/user/example": {
"github.com/user/example/first",
"github.com/user/example/second",
},
},
"/src", "/data",
},
1: {
Control{FS: trees[1], Hidden: true},
map[string][]string{
"github.com/user/example/dir": nil,
"github.com/user/example": {
"github.com/user/example/first",
"github.com/user/example/second",
},
},
"/src", "/data",
},
2: {
Control{FS: trees[0]},
map[string][]string{
"github.com/user/example": {
"github.com/user/example/assets",
},
"github.com/user/example/dir": nil,
},
"/src", "/data",
},
3: {
Control{FS: trees[0], Hidden: true},
map[string][]string{
"github.com/user/example/.git": nil,
"github.com/user/example": {
"github.com/user/example/assets",
},
"github.com/user/example/dir": nil,
},
"/src", "/data",
},
4: {
Control{FS: trees[2]},
map[string][]string{
"licstat/schema": nil,
},
"/src", "/schema",
},
5: {
Control{FS: trees[2], Hidden: true},
map[string][]string{
"licstat/schema": nil,
},
"/src", "/schema",
},
}
for i, cas := range cases {
if i != 3 {
continue
}
diff := cas.c.IntersectInclude(
filepath.FromSlash(cas.src),
filepath.FromSlash(cas.dst),
)
if len(diff) == 0 {
t.Errorf("want len(diff)!=0 (i=%d)", i)
continue
}
if !equaldiff(diff, cas.diff) {
t.Errorf("want diff=%v; got %v (i=%d)", cas.diff, diff, i)
}
}
}

func TestFind(t *testing.T) {
t.Skip("TODO(rjeczalik)")
}

0 comments on commit c5ee714

Please sign in to comment.