Skip to content
This repository has been archived by the owner on Mar 29, 2023. It is now read-only.

Refactor filename - file relation #2

Merged
merged 25 commits into from
Dec 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gx/lastpubver
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.1: QmZMWMvWMVKCbHetJ4RgndbuEF1io2UpUxwQwtNjtYPzSC
2.0.3: QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX
1 change: 1 addition & 0 deletions ci/Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
golang()
102 changes: 67 additions & 35 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,88 @@ import (
)

var (
ErrNotDirectory = errors.New("Couldn't call NextFile(), this isn't a directory")
ErrNotReader = errors.New("This file is a directory, can't use Reader functions")
ErrNotDirectory = errors.New("file isn't a directory")
ErrNotReader = errors.New("file isn't a regular file")

ErrNotSupported = errors.New("operation not supported")
)

// File is an interface that provides functionality for handling
// files/directories as values that can be supplied to commands. For
// directories, child files are accessed serially by calling `NextFile()`.
// Node is a common interface for files, directories and other special files
type Node interface {
io.Closer

// Size returns size of this file (if this file is a directory, total size of
// all files stored in the tree should be returned). Some implementations may
// choose not to implement this
Size() (int64, error)
magik6k marked this conversation as resolved.
Show resolved Hide resolved
}

// Node represents a regular Unix file
type File interface {
// Files implement ReadCloser, but can only be read from or closed if
// they are not directories
io.ReadCloser

// FileName returns a filename associated with this file
FileName() string

// FullPath returns the full path used when adding with this file
FullPath() string

// IsDirectory returns true if the File is a directory (and therefore
// supports calling `NextFile`) and false if the File is a normal file
// (and therefor supports calling `Read` and `Close`)
IsDirectory() bool

// NextFile returns the next child file available (if the File is a
// directory). It will return (nil, io.EOF) if no more files are
// available. If the file is a regular file (not a directory), NextFile
// will return a non-nil error.
NextFile() (File, error)
Node

io.Reader
io.Seeker
}

type StatFile interface {
File
// DirEntry exposes information about a directory entry
type DirEntry interface {
// Name returns base name of this entry, which is the base name of referenced
// file
Name() string

Stat() os.FileInfo
// Node returns the file referenced by this DirEntry
Node() Node
}

type PeekFile interface {
SizeFile
// DirIterator is a iterator over directory entries.
// See Directory.Entries for more
type DirIterator interface {
// DirEntry holds information about current directory entry.
// Note that after creating new iterator you MUST call Next() at least once
// before accessing these methods. Calling these methods without prior calls
// to Next() and after Next() returned false may result in undefined behavior
magik6k marked this conversation as resolved.
Show resolved Hide resolved
DirEntry

// Next advances iterator to the next file.
Next() bool

Peek(n int) File
Length() int
// Err may return an error after previous call to Next() returned `false`.
// If previous call to Next() returned `true`, Err() is guaranteed to
// return nil
Err() error
}

type SizeFile interface {
File
// Directory is a special file which can link to any number of files.
type Directory interface {
Node

Size() (int64, error)
// Entries returns a stateful iterator over directory entries.
//
// Example usage:
//
// it := dir.Entries()
// for it.Next() {
// name := it.Name()
// file := it.Node()
// [...]
// }
// if it.Err() != nil {
// return err
// }
//
// Note that you can't store the result of it.Node() and use it after
// advancing the iterator
Entries() DirIterator
}

// FileInfo exposes information on files in local filesystem
type FileInfo interface {
Node

// AbsPath returns full real file path.
AbsPath() string

// Stat returns os.Stat of this file, may be nil for some files
Stat() os.FileInfo
}
127 changes: 51 additions & 76 deletions file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,53 @@ package files

import (
"io"
"io/ioutil"
"mime/multipart"
"strings"
"testing"
)

func TestSliceFiles(t *testing.T) {
name := "testname"
files := []File{
NewReaderFile("file.txt", "file.txt", ioutil.NopCloser(strings.NewReader("Some text!\n")), nil),
NewReaderFile("beep.txt", "beep.txt", ioutil.NopCloser(strings.NewReader("beep")), nil),
NewReaderFile("boop.txt", "boop.txt", ioutil.NopCloser(strings.NewReader("boop")), nil),
}
sf := NewMapDirectory(map[string]Node{
"1": NewBytesFile([]byte("Some text!\n")),
"2": NewBytesFile([]byte("beep")),
"3": NewBytesFile([]byte("boop")),
})
buf := make([]byte, 20)

sf := NewSliceFile(name, name, files)

if !sf.IsDirectory() {
t.Fatal("SliceFile should always be a directory")
}

if n, err := sf.Read(buf); n > 0 || err != io.EOF {
t.Fatal("Shouldn't be able to read data from a SliceFile")
}
it := sf.Entries()

if err := sf.Close(); err != ErrNotReader {
t.Fatal("Shouldn't be able to call `Close` on a SliceFile")
if !it.Next() {
t.Fatal("Expected a file")
}

file, err := sf.NextFile()
if file == nil || err != nil {
t.Fatal("Expected a file and nil error")
rf := ToFile(it.Node())
if rf == nil {
t.Fatal("Expected a regular file")
}
read, err := file.Read(buf)
read, err := rf.Read(buf)
if read != 11 || err != nil {
t.Fatal("NextFile got a file in the wrong order")
}

file, err = sf.NextFile()
if file == nil || err != nil {
t.Fatal("Expected a file and nil error")
if !it.Next() {
t.Fatal("Expected a file")
}
file, err = sf.NextFile()
if file == nil || err != nil {
t.Fatal("Expected a file and nil error")
if !it.Next() {
t.Fatal("Expected a file")
}
if it.Next() {
t.Fatal("Wild file appeared!")
}

file, err = sf.NextFile()
if file != nil || err != io.EOF {
t.Fatal("Expected a nil file and io.EOF")
if err := sf.Close(); err != nil {
t.Fatal("Should be able to call `Close` on a SliceFile")
}
}

func TestReaderFiles(t *testing.T) {
message := "beep boop"
rf := NewReaderFile("file.txt", "file.txt", ioutil.NopCloser(strings.NewReader(message)), nil)
rf := NewBytesFile([]byte(message))
buf := make([]byte, len(message))

if rf.IsDirectory() {
t.Fatal("ReaderFile should never be a directory")
}
file, err := rf.NextFile()
if file != nil || err != ErrNotDirectory {
t.Fatal("Expected a nil file and ErrNotDirectory")
}

if n, err := rf.Read(buf); n == 0 || err != nil {
t.Fatal("Expected to be able to read")
}
Expand Down Expand Up @@ -114,23 +95,21 @@ anotherfile
if part == nil || err != nil {
t.Fatal("Expected non-nil part, nil error")
}
mpf, err := NewFileFromPart(part)
mpname, mpf, err := newFileFromPart("", part, &peekReader{r: mpReader})
if mpf == nil || err != nil {
t.Fatal("Expected non-nil MultipartFile, nil error")
t.Fatal("Expected non-nil multipartFile, nil error")
}
if mpf.IsDirectory() {
mf, ok := mpf.(File)
if !ok {
t.Fatal("Expected file to not be a directory")
}
if mpf.FileName() != "name" {
if mpname != "name" {
t.Fatal("Expected filename to be \"name\"")
}
if file, err := mpf.NextFile(); file != nil || err != ErrNotDirectory {
t.Fatal("Expected a nil file and ErrNotDirectory")
}
if n, err := mpf.Read(buf); n != 4 || !(err == io.EOF || err == nil) {
if n, err := mf.Read(buf); n != 4 || !(err == io.EOF || err == nil) {
t.Fatal("Expected to be able to read 4 bytes", n, err)
}
if err := mpf.Close(); err != nil {
if err := mf.Close(); err != nil {
t.Fatal("Expected to be able to close file")
}

Expand All @@ -139,39 +118,38 @@ anotherfile
if part == nil || err != nil {
t.Fatal("Expected non-nil part, nil error")
}
mpf, err = NewFileFromPart(part)
mpname, mpf, err = newFileFromPart("", part, &peekReader{r: mpReader})
if mpf == nil || err != nil {
t.Fatal("Expected non-nil MultipartFile, nil error")
t.Fatal("Expected non-nil multipartFile, nil error")
}
if !mpf.IsDirectory() {
md, ok := mpf.(Directory)
if !ok {
t.Fatal("Expected file to be a directory")
}
if mpf.FileName() != "dir" {
if mpname != "dir" {
t.Fatal("Expected filename to be \"dir\"")
}
if n, err := mpf.Read(buf); n > 0 || err != ErrNotReader {
t.Fatal("Shouldn't be able to call `Read` on a directory")
}
if err := mpf.Close(); err != ErrNotReader {
t.Fatal("Shouldn't be able to call `Close` on a directory")
if err := md.Close(); err != nil {
t.Fatal("Should be able to call `Close` on a directory")
}

// test properties of file created from third part (nested file)
part, err = mpReader.NextPart()
if part == nil || err != nil {
t.Fatal("Expected non-nil part, nil error")
}
mpf, err = NewFileFromPart(part)
mpname, mpf, err = newFileFromPart("dir/", part, &peekReader{r: mpReader})
if mpf == nil || err != nil {
t.Fatal("Expected non-nil MultipartFile, nil error")
t.Fatal("Expected non-nil multipartFile, nil error")
}
if mpf.IsDirectory() {
t.Fatal("Expected file, got directory")
mf, ok = mpf.(File)
if !ok {
t.Fatal("Expected file to not be a directory")
}
if mpf.FileName() != "dir/nested" {
t.Fatalf("Expected filename to be \"nested\", got %s", mpf.FileName())
if mpname != "nested" {
t.Fatalf("Expected filename to be \"nested\", got %s", mpname)
}
if n, err := mpf.Read(buf); n != 12 || !(err == nil || err == io.EOF) {
if n, err := mf.Read(buf); n != 12 || !(err == nil || err == io.EOF) {
t.Fatalf("expected to be able to read 12 bytes from file: %s (got %d)", err, n)
}
if err := mpf.Close(); err != nil {
Expand All @@ -183,21 +161,18 @@ anotherfile
if part == nil || err != nil {
t.Fatal("Expected non-nil part, nil error")
}
mpf, err = NewFileFromPart(part)
mpname, mpf, err = newFileFromPart("dir/", part, &peekReader{r: mpReader})
if mpf == nil || err != nil {
t.Fatal("Expected non-nil MultipartFile, nil error")
t.Fatal("Expected non-nil multipartFile, nil error")
}
if mpf.IsDirectory() {
t.Fatal("Expected file to be a symlink")
ms, ok := mpf.(*Symlink)
if !ok {
t.Fatal("Expected file to not be a directory")
}
if mpf.FileName() != "dir/simlynk" {
if mpname != "simlynk" {
t.Fatal("Expected filename to be \"dir/simlynk\"")
}
slink, ok := mpf.(*Symlink)
if !ok {
t.Fatalf("expected file to be a symlink")
}
if slink.Target != "anotherfile" {
if ms.Target != "anotherfile" {
t.Fatal("expected link to point to anotherfile")
}
}
5 changes: 2 additions & 3 deletions is_hidden.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import (
"strings"
)

func IsHidden(f File) bool {

fName := filepath.Base(f.FileName())
func IsHidden(name string, f Node) bool {
fName := filepath.Base(name)

if strings.HasPrefix(fName, ".") && len(fName) > 1 {
return true
Expand Down
11 changes: 8 additions & 3 deletions is_hidden_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,20 @@ import (
windows "golang.org/x/sys/windows"
)

func IsHidden(f File) bool {
func IsHidden(name string, f Node) bool {

fName := filepath.Base(f.FileName())
fName := filepath.Base(name)

if strings.HasPrefix(fName, ".") && len(fName) > 1 {
return true
}

p, e := windows.UTF16PtrFromString(f.FullPath())
fi, ok := f.(FileInfo)
if !ok {
return false
}

p, e := windows.UTF16PtrFromString(fi.AbsPath())
if e != nil {
return false
}
Expand Down
Loading