Skip to content

Commit

Permalink
Merge pull request #6 from moov-io/impl-list-files-with-walk
Browse files Browse the repository at this point in the history
fix: implement ListFiles with Walk to avoid some error cases
  • Loading branch information
adamdecaf authored Aug 14, 2023
2 parents 250847b + b0a3b6d commit 91097a3
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 35 deletions.
53 changes: 22 additions & 31 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func (cc *client) Open(path string) (*File, error) {
// Callers need to close the returned Contents
//
// Callers should be aware that network errors while reading can occur since contents
// are streamed from the FTP server.
// are streamed from the FTP server. Having multiple open readers may not be supported.
func (cc *client) Reader(path string) (*File, error) {
cc.mu.Lock()
defer cc.mu.Unlock()
Expand Down Expand Up @@ -272,44 +272,35 @@ func (cc *client) UploadFile(path string, contents io.ReadCloser) (err error) {
}

func (cc *client) ListFiles(dir string) ([]string, error) {
cc.mu.Lock()
defer cc.mu.Unlock()

conn, err := cc.connection()
if err != nil {
return nil, err
pattern := filepath.Clean(strings.TrimPrefix(dir, string(os.PathSeparator)))
switch {
case pattern == ".":
if dir == "" {
pattern = "*"
} else {
pattern = filepath.Join(dir, "*")
}
case pattern != "":
pattern += "/*"
}

if dir != "" && dir != "." {
// Jump to previous directory after command is done
wd, err := conn.CurrentDir()
var filenames []string
err := cc.Walk(".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil, err
return err
}
defer func(previous string) {
// Return to our previous directory when initially called
if cleanupErr := conn.ChangeDir(previous); cleanupErr != nil {
err = fmt.Errorf("FTP: problem listing %s: %w", dir, cleanupErr)
}
}(wd)

// Move into directory to run the command
if err := conn.ChangeDir(dir); err != nil {
return nil, err
if d.IsDir() {
return nil
}
}

fds, err := conn.List("")
matches, err := filepath.Match(pattern, path)
if matches && err == nil {
filenames = append(filenames, filepath.Base(path))
}
return err
})
if err != nil {
return nil, fmt.Errorf("listing %s failed: %w", dir, err)
}

var filenames []string
for i := range fds {
if fds[i].Type == ftp.EntryTypeFile {
filenames = append(filenames, filepath.Base(fds[i].Name))
}
}
return filenames, nil
}

Expand Down
23 changes: 20 additions & 3 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,21 @@ func TestClient(t *testing.T) {
t.Run("list", func(t *testing.T) {
filenames, err := client.ListFiles(".")
require.NoError(t, err)
require.ElementsMatch(t, filenames, []string{"first.txt", "second.txt"})
require.ElementsMatch(t, filenames, []string{"first.txt", "second.txt", "empty.txt"})
})

t.Run("list subdir", func(t *testing.T) {
filenames, err := client.ListFiles("archive")
require.NoError(t, err)
require.ElementsMatch(t, filenames, []string{"old.txt", "empty2.txt"})

filenames, err = client.ListFiles("/archive")
require.NoError(t, err)
require.ElementsMatch(t, filenames, []string{"old.txt", "empty2.txt"})

filenames, err = client.ListFiles("/archive/")
require.NoError(t, err)
require.ElementsMatch(t, filenames, []string{"old.txt", "empty2.txt"})
})

t.Run("walk", func(t *testing.T) {
Expand All @@ -85,7 +99,7 @@ func TestClient(t *testing.T) {
return nil
})
require.NoError(t, err)
require.ElementsMatch(t, found, []string{"first.txt", "second.txt", "archive/old.txt"})
require.ElementsMatch(t, found, []string{"first.txt", "second.txt", "empty.txt", "archive/old.txt", "archive/empty2.txt"})
})

require.NoError(t, client.Close())
Expand Down Expand Up @@ -123,15 +137,18 @@ func TestClientErrors(t *testing.T) {

t.Run("list", func(t *testing.T) {
filenames, err := client.ListFiles("does/not/exist")
require.ErrorContains(t, err, "550 Directory change to /does/not/exist failed: lstat /data/does/not/exist: no such file or directory")
require.NoError(t, err)
require.Len(t, filenames, 0)
})

t.Run("walk", func(t *testing.T) {
var found []string
err := client.Walk("does/not/exist", func(path string, d fs.DirEntry, err error) error {
found = append(found, path)
return nil
})
require.ErrorContains(t, err, "550 Directory change to /does/not/exist failed: lstat /data/does/not/exist: no such file or directory")
require.Len(t, found, 0)
})

require.NoError(t, client.Close())
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version: '3.7'

services:
ftp:
image: moov/fsftp:v0.2.1
image: moov/fsftp:v0.2.2
ports:
- "2121:2121"
- "30000-30009:30000-30009"
Expand Down
Empty file.
Empty file added testdata/ftp-server/empty.txt
Empty file.

0 comments on commit 91097a3

Please sign in to comment.