From 3b6697e5b6d072fa4c4d23963db8712decbafffd Mon Sep 17 00:00:00 2001 From: Viorel Craescu Date: Sat, 25 Apr 2020 19:27:59 +0300 Subject: [PATCH] fix_walk --- ftp.go | 1 + walker.go | 69 ++++++++++++++++++++---------- walker_test.go | 114 ++++++++++++++++++++++++------------------------- 3 files changed, 104 insertions(+), 80 deletions(-) diff --git a/ftp.go b/ftp.go index 341e58d..faf1c9c 100644 --- a/ftp.go +++ b/ftp.go @@ -760,6 +760,7 @@ func (c *ServerConn) Walk(root string) *Walker { } w.root = root + w.descend = true return w } diff --git a/walker.go b/walker.go index dacd0d8..5f16519 100644 --- a/walker.go +++ b/walker.go @@ -1,7 +1,9 @@ package ftp import ( - pa "path" + "fmt" + "os" + "path" "strings" ) @@ -9,14 +11,14 @@ import ( type Walker struct { serverConn *ServerConn root string - cur item - stack []item + cur *item + stack []*item descend bool } type item struct { path string - entry Entry + entry *Entry err error } @@ -24,36 +26,59 @@ type item struct { // which will then be available through the Path, Stat, and Err methods. // It returns false when the walk stops at the end of the tree. func (w *Walker) Next() bool { - if w.descend && w.cur.err == nil && w.cur.entry.Type == EntryTypeFolder { - list, err := w.serverConn.List(w.cur.path) - if err != nil { - w.cur.err = err - w.stack = append(w.stack, w.cur) - } else { - for i := len(list) - 1; i >= 0; i-- { - if !strings.HasSuffix(w.cur.path, "/") { - w.cur.path += "/" - } + var isRoot bool + if w.cur == nil { + w.cur = &item{ + path: strings.Trim(w.root, string(os.PathSeparator)), + } - var path string - if list[i].Type == EntryTypeFolder { - path = pa.Join(w.cur.path, list[i].Name) - } else { - path = w.cur.path - } + isRoot = true + } - w.stack = append(w.stack, item{path, *list[i], nil}) + entries, err := w.serverConn.List(w.cur.path) + w.cur.err = err + if err == nil { + if len(entries) == 0 { + w.cur.err = fmt.Errorf("no such file or directory: %s", w.cur.path) + + return false + } + + if isRoot && len(entries) == 1 && entries[0].Type == EntryTypeFile { + w.cur.err = fmt.Errorf("root is not a directory: %s", w.cur.path) + + return false + } + + for i, entry := range entries { + if entry.Name == "." || (i == 0 && entry.Type == EntryTypeFile) { + entry.Name = path.Base(w.cur.path) + w.cur.entry = entry + continue } + + if entry.Name == ".." || !w.descend { + continue + } + + item := &item{ + path: path.Join(w.cur.path, entry.Name), + entry: entry, + } + + w.stack = append(w.stack, item) } } if len(w.stack) == 0 { return false } + i := len(w.stack) - 1 w.cur = w.stack[i] w.stack = w.stack[:i] w.descend = true + return true } @@ -71,7 +96,7 @@ func (w *Walker) Err() error { // Stat returns info for the most recent file or directory // visited by a call to Step. -func (w *Walker) Stat() Entry { +func (w *Walker) Stat() *Entry { return w.cur.entry } diff --git a/walker_test.go b/walker_test.go index f4a4aeb..d557553 100644 --- a/walker_test.go +++ b/walker_test.go @@ -2,7 +2,6 @@ package ftp import ( "fmt" - "strings" "testing" "time" @@ -29,10 +28,10 @@ func TestWalkReturnsCorrectlyPopulatedWalker(t *testing.T) { func TestFieldsReturnCorrectData(t *testing.T) { w := Walker{ - cur: item{ + cur: &item{ path: "/root/", - err: fmt.Errorf("This is an error"), - entry: Entry{ + err: fmt.Errorf("this is an error"), + entry: &Entry{ Name: "root", Size: 123, Time: time.Now(), @@ -41,7 +40,7 @@ func TestFieldsReturnCorrectData(t *testing.T) { }, } - assert.Equal(t, "This is an error", w.Err().Error()) + assert.Equal(t, "this is an error", w.Err().Error()) assert.Equal(t, "/root/", w.Path()) assert.Equal(t, EntryTypeFolder, w.Stat().Type) } @@ -55,11 +54,22 @@ func TestSkipDirIsCorrectlySet(t *testing.T) { } func TestNoDescendDoesNotAddToStack(t *testing.T) { - w := new(Walker) - w.cur = item{ + mock, err := newFtpMock(t, "127.0.0.1") + if err != nil { + t.Fatal(err) + } + defer mock.Close() + + c, cErr := Connect(mock.Addr()) + if cErr != nil { + t.Fatal(err) + } + + w := c.Walk("/root") + w.cur = &item{ path: "/root/", err: nil, - entry: Entry{ + entry: &Entry{ Name: "root", Size: 123, Time: time.Now(), @@ -67,11 +77,11 @@ func TestNoDescendDoesNotAddToStack(t *testing.T) { }, } - w.stack = []item{ - item{ + w.stack = []*item{ + &item{ path: "file", err: nil, - entry: Entry{ + entry: &Entry{ Name: "file", Size: 123, Time: time.Now(), @@ -90,11 +100,23 @@ func TestNoDescendDoesNotAddToStack(t *testing.T) { } func TestEmptyStackReturnsFalse(t *testing.T) { - w := new(Walker) - w.cur = item{ + mock, err := newFtpMock(t, "127.0.0.1") + if err != nil { + t.Fatal(err) + } + defer mock.Close() + + c, cErr := Connect(mock.Addr()) + if cErr != nil { + t.Fatal(err) + } + + w := c.Walk("/root") + + w.cur = &item{ path: "/root/", err: nil, - entry: Entry{ + entry: &Entry{ Name: "root", Size: 123, Time: time.Now(), @@ -102,7 +124,7 @@ func TestEmptyStackReturnsFalse(t *testing.T) { }, } - w.stack = []item{} + w.stack = []*item{} w.SkipDir() @@ -112,11 +134,22 @@ func TestEmptyStackReturnsFalse(t *testing.T) { } func TestCurAndStackSetCorrectly(t *testing.T) { - w := new(Walker) - w.cur = item{ + mock, err := newFtpMock(t, "127.0.0.1") + if err != nil { + t.Fatal(err) + } + defer mock.Close() + + c, cErr := Connect(mock.Addr()) + if cErr != nil { + t.Fatal(err) + } + + w := c.Walk("/root") + w.cur = &item{ path: "root/file1", err: nil, - entry: Entry{ + entry: &Entry{ Name: "file1", Size: 123, Time: time.Now(), @@ -124,21 +157,21 @@ func TestCurAndStackSetCorrectly(t *testing.T) { }, } - w.stack = []item{ - item{ + w.stack = []*item{ + &item{ path: "file", err: nil, - entry: Entry{ + entry: &Entry{ Name: "file", Size: 123, Time: time.Now(), Type: EntryTypeFile, }, }, - item{ + &item{ path: "root/file1", err: nil, - entry: Entry{ + entry: &Entry{ Name: "file1", Size: 123, Time: time.Now(), @@ -154,38 +187,3 @@ func TestCurAndStackSetCorrectly(t *testing.T) { assert.Equal(t, 0, len(w.stack)) assert.Equal(t, "file", w.cur.entry.Name) } - -func TestStackIsPopulatedCorrectly(t *testing.T) { - - mock, err := newFtpMock(t, "127.0.0.1") - if err != nil { - t.Fatal(err) - } - defer mock.Close() - - c, cErr := Connect(mock.Addr()) - if cErr != nil { - t.Fatal(err) - } - - w := Walker{ - cur: item{ - path: "/root", - entry: Entry{ - Name: "root", - Size: 123, - Time: time.Now(), - Type: EntryTypeFolder, - }, - }, - serverConn: c, - } - - w.descend = true - - w.Next() - - assert.Equal(t, 0, len(w.stack)) - assert.Equal(t, "lo", w.cur.entry.Name) - assert.Equal(t, true, strings.HasSuffix(w.cur.path, "/")) -}