mirror of
https://github.com/kopia/kopia.git
synced 2026-05-18 11:44:36 -04:00
Added lister
This commit is contained in:
14
dir/entry.go
14
dir/entry.go
@@ -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
95
dir/lister.go
Normal 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
19
dir/lister_nonwindows.go
Normal 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
80
dir/lister_test.go
Normal 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
7
dir/lister_windows.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package dir
|
||||
|
||||
import "os"
|
||||
|
||||
func populatePlatformSpecificEntryDetails(e *Entry, fileInfo os.FileInfo) error {
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user