Added lister

This commit is contained in:
Jarek Kowalski
2016-03-25 11:53:54 -07:00
parent 0c60ec9861
commit 28b0d3d47f
5 changed files with 214 additions and 1 deletions

View File

@@ -1,6 +1,7 @@
package dir
import (
"fmt"
"io"
"os"
"time"
@@ -54,6 +55,9 @@ func FileModeToType(mode os.FileMode) EntryType {
}
}
// Opener opens the contents of directory entry for reading.
type Opener func() (io.ReadCloser, error)
// Entry stores attributes of a single entry in a directory.
type Entry struct {
Name string
@@ -64,7 +68,15 @@ type Entry struct {
UserID uint32
GroupID uint32
ObjectID content.ObjectID
Open func() (io.ReadCloser, error)
Open Opener
}
func (e *Entry) String() string {
return fmt.Sprintf(
"name: '%v' type: %v modTime: %v size: %v oid: '%v' uid: %v gid: %v",
e.Name, e.Type, e.ModTime, e.Size, e.ObjectID, e.UserID, e.GroupID,
)
}
// Directory contains access to contents of directory, both in original order and indexed by name.

95
dir/lister.go Normal file
View File

@@ -0,0 +1,95 @@
package dir
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
)
// Listing encapsulates list of items in a directory.
type Listing struct {
Directories []*Entry
Files []*Entry
}
func (l Listing) String() string {
s := ""
for i, d := range l.Directories {
s += fmt.Sprintf("dir[%v] = %v\n", i, d)
}
for i, f := range l.Files {
s += fmt.Sprintf("file[%v] = %v\n", i, f)
}
return s
}
// Lister lists contents of filesystem directories.
type Lister interface {
List(path string) (Listing, error)
}
type filesystemLister struct {
}
func (l *filesystemLister) List(path string) (Listing, error) {
listing := Listing{}
entries, err := ioutil.ReadDir(path)
if err != nil {
return listing, err
}
for _, fi := range entries {
switch fi.Name() {
case ".":
case "..":
continue
}
e, err := entryFromFileSystemInfo(path, fi)
if err != nil {
return listing, err
}
if e.Type == EntryTypeDirectory {
listing.Directories = append(listing.Directories, e)
} else {
listing.Files = append(listing.Files, e)
}
}
return listing, nil
}
// NewFilesystemLister creates a Lister that can be used to list contents of filesystem directories.
func NewFilesystemLister() Lister {
return &filesystemLister{}
}
func openFileFunc(parentDir string, fi os.FileInfo) Opener {
return func() (io.ReadCloser, error) {
return os.Open(filepath.Join(parentDir, fi.Name()))
}
}
func entryFromFileSystemInfo(parentDir string, fi os.FileInfo) (*Entry, error) {
e := &Entry{
Name: fi.Name(),
Mode: int16(fi.Mode().Perm()),
ModTime: fi.ModTime(),
Type: FileModeToType(fi.Mode()),
Open: openFileFunc(parentDir, fi),
}
if e.Type == EntryTypeFile {
e.Size = fi.Size()
}
if err := populatePlatformSpecificEntryDetails(e, fi); err != nil {
return nil, err
}
return e, nil
}

19
dir/lister_nonwindows.go Normal file
View File

@@ -0,0 +1,19 @@
// +build !windows
package dir
import (
"fmt"
"os"
"syscall"
)
func populatePlatformSpecificEntryDetails(e *Entry, fileInfo os.FileInfo) error {
if stat, ok := fileInfo.Sys().(*syscall.Stat_t); ok {
e.UserID = stat.Uid
e.GroupID = stat.Gid
return nil
}
return fmt.Errorf("unable to retrieve platform-specific file information")
}

80
dir/lister_test.go Normal file
View File

@@ -0,0 +1,80 @@
package dir
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
"testing"
)
func TestLister(t *testing.T) {
var err error
tmp, err := ioutil.TempDir("", "kopia")
if err != nil {
t.Errorf("cannot create temp directory: %v", err)
return
}
defer os.RemoveAll(tmp)
lister := NewFilesystemLister()
var listing Listing
// Try listing directory that does not exist.
listing, err = lister.List(fmt.Sprintf("/no-such-dir-%v", time.Now().Nanosecond()))
if err == nil {
t.Errorf("expected error when listing directory that does not exist.")
}
// Now list an empty directory that does not exist.
listing, err = lister.List(tmp)
if err != nil {
t.Errorf("error when listing empty directory: %v", err)
}
if len(listing.Directories)+len(listing.Files) > 0 {
t.Errorf("expected empty directory, got %v", listing)
}
// Now list a directory with 3 files.
ioutil.WriteFile(filepath.Join(tmp, "f3"), []byte{1, 2, 3}, 0777)
ioutil.WriteFile(filepath.Join(tmp, "f2"), []byte{1, 2, 3, 4}, 0777)
ioutil.WriteFile(filepath.Join(tmp, "f1"), []byte{1, 2, 3, 4, 5}, 0777)
os.Mkdir(filepath.Join(tmp, "z"), 0777)
os.Mkdir(filepath.Join(tmp, "y"), 0777)
listing, err = lister.List(tmp)
if err != nil {
t.Errorf("error when listing directory with files: %v", err)
}
if len(listing.Files) != 3 || len(listing.Directories) != 2 {
t.Errorf("expected 3 files, got: %v", listing)
} else {
goodCount := 0
if listing.Files[0].Name == "f1" && listing.Files[0].Size == 5 && listing.Files[0].Type == EntryTypeFile {
goodCount++
}
if listing.Files[1].Name == "f2" && listing.Files[1].Size == 4 && listing.Files[1].Type == EntryTypeFile {
goodCount++
}
if listing.Files[2].Name == "f3" && listing.Files[2].Size == 3 && listing.Files[2].Type == EntryTypeFile {
goodCount++
}
if listing.Directories[0].Name == "y" && listing.Directories[0].Size == 0 && listing.Directories[0].Type == EntryTypeDirectory {
goodCount++
}
if listing.Directories[1].Name == "z" && listing.Directories[1].Size == 0 && listing.Directories[1].Type == EntryTypeDirectory {
goodCount++
}
if goodCount != 5 {
t.Errorf("invalid listing data:\n%v", listing)
}
}
}

7
dir/lister_windows.go Normal file
View File

@@ -0,0 +1,7 @@
package dir
import "os"
func populatePlatformSpecificEntryDetails(e *Entry, fileInfo os.FileInfo) error {
return nil
}