Skip to content

Commit

Permalink
Merge pull request #15 from LandonTClipp/stat
Browse files Browse the repository at this point in the history
Breaking out stat into separate functions
  • Loading branch information
LandonTClipp authored Jul 17, 2020
2 parents d2cf200 + cd0e2cc commit 7c25c21
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 14 deletions.
77 changes: 63 additions & 14 deletions path.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ func (p *Path) Fs() afero.Fs {
}

func (p *Path) doesNotImplementErr(interfaceName string) error {
return errors.Wrapf(ErrDoesNotImplement, "Path's afero filesystem %s does not implement %s", getFsName(p.fs), interfaceName)
return doesNotImplementErr(interfaceName, p.Fs())
}

func doesNotImplementErr(interfaceName string, fs afero.Fs) error {
return errors.Wrapf(ErrDoesNotImplement, "Path's afero filesystem %s does not implement %s", getFsName(fs), interfaceName)
}

// *******************************
Expand Down Expand Up @@ -184,6 +188,12 @@ func (p *Path) IsDir() (bool, error) {
return afero.IsDir(p.Fs(), p.Path())
}

// IsDir returns whether or not the os.FileInfo object represents a
// directory.
func IsDir(fileInfo os.FileInfo) bool {
return fileInfo.IsDir()
}

// IsEmpty checks if a given file or directory is empty.
func (p *Path) IsEmpty() (bool, error) {
return afero.IsEmpty(p.Fs(), p.Path())
Expand Down Expand Up @@ -396,6 +406,20 @@ func (p *Path) RelativeTo(other *Path) (*Path, error) {
return NewPathAfero(strings.Join(relativePath, "/"), p.Fs()), nil
}

// Lstat lstat's the path if the underlying afero filesystem supports it. If
// the filesystem does not support afero.Lstater, an error will be returned.
// A nil os.FileInfo is returned on errors. Also returned is a boolean describing
// whether or not Lstat was called (in cases where the filesystem is an OS filesystem)
// or not called (in cases where only Stat is supported). See
// https://godoc.org/github.com/spf13/afero#Lstater for more info.
func (p *Path) Lstat() (os.FileInfo, bool, error) {
lStater, ok := p.Fs().(afero.Lstater)
if !ok {
return nil, false, p.doesNotImplementErr("afero.Lstater")
}
return lStater.LstatIfPossible(p.Path())
}

// *********************************
// * filesystem-specific functions *
// *********************************
Expand All @@ -422,32 +446,36 @@ func (p *Path) String() string {

// IsFile returns true if the given path is a file.
func (p *Path) IsFile() (bool, error) {
fileInfo, err := p.Fs().Stat(p.Path())
fileInfo, err := p.Stat()
if err != nil {
return false, err
}
return IsFile(fileInfo)
}

// IsFile returns whether or not the file described by the given
// os.FileInfo is a regular file.
func IsFile(fileInfo os.FileInfo) (bool, error) {
return fileInfo.Mode().IsRegular(), nil
}

// 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) {
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
fileInfo, _, err := p.Lstat()
if err != nil {
return false, err
}
return IsSymlink(fileInfo)
}

isSymlink := false
// IsSymlink returns true if the file described by the given
// os.FileInfo describes a symlink.
func IsSymlink(fileInfo os.FileInfo) (bool, error) {
if fileInfo.Mode()&os.ModeSymlink != 0 {
isSymlink = true
return true, nil
}
return isSymlink, nil
return false, nil
}

// Path returns the string representation of the path
Expand Down Expand Up @@ -516,5 +544,26 @@ func (p *Path) Mtime() (time.Time, error) {
if err != nil {
return time.Time{}, err
}
return stat.ModTime(), nil
return Mtime(stat)
}

// Mtime returns the mtime described in the given os.FileInfo object
func Mtime(fileInfo os.FileInfo) (time.Time, error) {
return fileInfo.ModTime(), nil
}

// Size returns the size of the object. Fails if the object doesn't exist.
func (p *Path) Size() (int64, error) {
stat, err := p.Stat()
if err != nil {
return 0, err
}
return Size(stat), nil
}

// Size returns the size described by the os.FileInfo. Before you say anything,
// yes... you could just do fileInfo.Size(). This is purely a convenience function
// to create API consistency.
func Size(fileInfo os.FileInfo) int64 {
return fileInfo.Size()
}
33 changes: 33 additions & 0 deletions path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,39 @@ func (p *PathSuite) TestRenamePath() {
assert.False(p.T(), oldFileExists)
}

func (p *PathSuite) TestSizeZero() {
file := p.tmpdir.Join("file.txt")
require.NoError(p.T(), file.WriteFile([]byte{}, 0o644))
size, err := file.Size()
require.NoError(p.T(), err)
p.Zero(size)
}

func (p *PathSuite) TestSizeNonZero() {
msg := "oh, it's you"
file := p.tmpdir.Join("file.txt")
require.NoError(p.T(), file.WriteFile([]byte(msg), 0o644))
size, err := file.Size()
require.NoError(p.T(), err)
p.Equal(len(msg), int(size))
}

func (p *PathSuite) TestIsDir() {
dir := p.tmpdir.Join("dir")
require.NoError(p.T(), dir.Mkdir(0o755))
isDir, err := dir.IsDir()
require.NoError(p.T(), err)
p.True(isDir)
}

func (p *PathSuite) TestIsntDir() {
file := p.tmpdir.Join("file.txt")
require.NoError(p.T(), file.WriteFile([]byte("hello world!"), 0o644))
isDir, err := file.IsDir()
require.NoError(p.T(), err)
p.False(isDir)
}

func (p *PathSuite) TestGetLatest() {
now := time.Now()
for i := 0; i < 5; i++ {
Expand Down

0 comments on commit 7c25c21

Please sign in to comment.