Skip to content

Improved EquivalentTo() and added Canonical() #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 13, 2021
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
34 changes: 28 additions & 6 deletions paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,15 +524,22 @@ func (p *Path) EquivalentTo(other *Path) bool {
if p.Clean().path == other.Clean().path {
return true
}
absP, err := p.Abs()
if err != nil {
return false

if infoP, err := p.Stat(); err != nil {
// go ahead with the next test...
} else if infoOther, err := other.Stat(); err != nil {
// go ahead with the next test...
} else if os.SameFile(infoP, infoOther) {
return true
}
absOther, err := other.Abs()
if err != nil {

if absP, err := p.Abs(); err != nil {
return false
} else if absOther, err := other.Abs(); err != nil {
return false
} else {
return absP.path == absOther.path
}
return absP.path == absOther.path
}

// Parents returns all the parents directories of the current path. If the path is absolute
Expand All @@ -557,3 +564,18 @@ func (p *Path) Parents() []*Path {
func (p *Path) String() string {
return p.path
}

// Canonical return a "canonical" Path for the given filename.
// The meaning of "canonical" is OS-dependent but the goal of this method
// is to always return the same path for a given file (factoring out all the
// possibile ambiguities including, for example, relative paths traversal,
// symlinks, drive volume letter case, etc).
func (p *Path) Canonical() *Path {
canonical := p.Clone()
// https://github.com/golang/go/issues/17084#issuecomment-246645354
canonical.FollowSymLink()
if absPath, err := canonical.Abs(); err == nil {
canonical = absPath
}
return canonical
}
31 changes: 31 additions & 0 deletions paths_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ package paths

import (
"path/filepath"
"runtime"
"strings"
"testing"

Expand Down Expand Up @@ -311,6 +312,32 @@ func TestEquivalentPaths(t *testing.T) {
require.True(t, New("file1", "abc").EquivalentTo(New("file1", "abc", "def", "..")))
require.True(t, wd.Join("file1").EquivalentTo(New("file1")))
require.True(t, wd.Join("file1").EquivalentTo(New("file1", "abc", "..")))

if runtime.GOOS == "windows" {
q := New("_testdata", "anotherFile")
r := New("_testdata", "ANOTHE~1")
require.True(t, q.EquivalentTo(r))
require.True(t, r.EquivalentTo(q))
}
}

func TestCanonicalize(t *testing.T) {
wd, err := Getwd()
require.NoError(t, err)

p := New("_testdata", "anotherFile").Canonical()
require.Equal(t, wd.Join("_testdata", "anotherFile").String(), p.String())

p = New("_testdata", "nonexistentFile").Canonical()
require.Equal(t, wd.Join("_testdata", "nonexistentFile").String(), p.String())

if runtime.GOOS == "windows" {
q := New("_testdata", "ANOTHE~1").Canonical()
require.Equal(t, wd.Join("_testdata", "anotherFile").String(), q.String())

r := New("c:\\").Canonical()
require.Equal(t, "C:\\", r.String())
}
}

func TestRelativeTo(t *testing.T) {
Expand Down Expand Up @@ -341,6 +368,10 @@ func TestRelativeTo(t *testing.T) {

func TestWriteToTempFile(t *testing.T) {
tmpDir := New("_testdata", "tmp")
err := tmpDir.MkdirAll()
require.NoError(t, err)
defer tmpDir.RemoveAll()

tmpData := []byte("test")
tmp, err := WriteToTempFile(tmpData, tmpDir, "prefix")
defer tmp.Remove()
Expand Down