Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package pathlib

import "github.com/spf13/afero"

// File represents a file in the filesystem. It inherits the afero.File interface
// but might also include additional functionality.
type File struct {
afero.File
}
28 changes: 21 additions & 7 deletions path.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,21 @@ func (p *Path) MkdirAll(perm os.FileMode) error {
return p.Fs().MkdirAll(p.Path(), perm)
}

// Open opens a file, returning it or an error, if any happens.
func (p *Path) Open() (afero.File, error) {
return p.Fs().Open(p.Path())
// Open opens a file for read-only, returning it or an error, if any happens.
func (p *Path) Open() (*File, error) {
handle, err := p.Fs().Open(p.Path())
return &File{
File: handle,
}, err
}

// OpenFile opens a file using the given flags and the given mode.
func (p *Path) OpenFile(flag int, perm os.FileMode) (afero.File, error) {
return p.Fs().OpenFile(p.Path(), flag, perm)
// See the list of flags at: https://golang.org/pkg/os/#pkg-constants
func (p *Path) OpenFile(flag int, perm os.FileMode) (*File, error) {
handle, err := p.Fs().OpenFile(p.Path(), flag, perm)
return &File{
File: handle,
}, err
}

// Remove removes a file, returning an error, if any
Expand Down Expand Up @@ -342,9 +349,16 @@ func (p *Path) IsFile() (bool, error) {
}

// IsSymlink returns true if the given path is a symlink.
// Fails if the filesystem doesn't implement afero.Lstater.
func (p *Path) IsSymlink() (bool, error) {
fileInfo, err := p.Fs().Stat(p.Path())
if err != nil {
lStater, ok := p.Fs().(afero.Lstater)
if !ok {
return false, p.doesNotImplementErr("afero.Lstater")
}
fileInfo, lstatCalled, err := lStater.LstatIfPossible(p.Path())
if err != nil || !lstatCalled {
// If lstat wasn't called then the filesystem doesn't implement it.
// Thus, it isn't a symlink
return false, err
}

Expand Down
80 changes: 80 additions & 0 deletions path_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package pathlib

import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
Expand All @@ -21,6 +23,9 @@ type PathSuite struct {
}

func (p *PathSuite) SetupTest() {
// We actually can't use the MemMapFs because some of the tests
// are testing symlink behavior. We might want to split these
// tests out to use MemMapFs when possible.
tmpdir, err := ioutil.TempDir("", "")
require.NoError(p.T(), err)
p.tmpdir = NewPath(tmpdir)
Expand Down Expand Up @@ -150,6 +155,81 @@ func (p *PathSuite) TestGetLatestEmpty() {
assert.Nil(p.T(), latest)
}

func (p *PathSuite) TestOpen() {
msg := "cubs > cardinals"
file := p.tmpdir.Join("file.txt")
require.NoError(p.T(), file.WriteFile([]byte(msg), 0o644))
fileHandle, err := file.Open()
require.NoError(p.T(), err)

readBytes := make([]byte, len(msg)+5)
n, err := fileHandle.Read(readBytes)
p.Equal(len(msg), n)
p.Equal(msg, string(readBytes[0:n]))
}

func (p *PathSuite) TestOpenFile() {
file := p.tmpdir.Join("file.txt")
fileHandle, err := file.OpenFile(os.O_RDWR|os.O_CREATE, 0o644)
require.NoError(p.T(), err)

msg := "do you play croquet?"
n, err := fileHandle.WriteString(msg)
p.Equal(len(msg), n)
p.NoError(err)

bytes := make([]byte, len(msg)+5)
n, err = fileHandle.ReadAt(bytes, 0)
p.Equal(len(msg), n)
p.True(errors.Is(err, io.EOF))
p.Equal(msg, string(bytes[0:n]))
}

func (p *PathSuite) TestDirExists() {
dir1 := p.tmpdir.Join("subdir")
exists, err := dir1.DirExists()
require.NoError(p.T(), err)
p.False(exists)

require.NoError(p.T(), dir1.Mkdir(0o755))
exists, err = dir1.DirExists()
require.NoError(p.T(), err)
p.True(exists)
}

func (p *PathSuite) TestIsFile() {
file1 := p.tmpdir.Join("file.txt")

require.NoError(p.T(), file1.WriteFile([]byte(""), 0o644))
exists, err := file1.IsFile()
require.NoError(p.T(), err)
p.True(exists)
}

func (p *PathSuite) TestIsEmpty() {
file1 := p.tmpdir.Join("file.txt")

require.NoError(p.T(), file1.WriteFile([]byte(""), 0o644))
isEmpty, err := file1.IsEmpty()
require.NoError(p.T(), err)
p.True(isEmpty)
}

func (p *PathSuite) TestIsSymlink() {
file1 := p.tmpdir.Join("file.txt")
require.NoError(p.T(), file1.WriteFile([]byte(""), 0o644))

symlink := p.tmpdir.Join("symlink")
p.NoError(symlink.Symlink(file1))
isSymlink, err := symlink.IsSymlink()
require.NoError(p.T(), err)
p.True(isSymlink)

stat, _ := symlink.Stat()
p.T().Logf("%v", stat.Mode())
p.T().Logf(symlink.Path())
}

func TestPathSuite(t *testing.T) {
suite.Run(t, new(PathSuite))
}
Expand Down