Skip to content

Commit 097e48a

Browse files
authored
Make DeepFS.SplitPath public & fix nested ext bug (#26)
* Add PathIsArchive function & refactor PathIsArchive checks to see if a path ends in an archive file. This simplifies one of the checks in the code and also fixes a small bug in DeepFS where path.Ext() wouldn't return the full extension for archive types with nested extensions: path.Ext("file.tar.gz") returns ".gz" instead of ".tar.gz" * Make SplitPath public So callers can split the path based on logic defined in the FS
1 parent f147267 commit 097e48a

File tree

2 files changed

+36
-20
lines changed

2 files changed

+36
-20
lines changed

Diff for: fs.go

+35-19
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ func (fsys *DeepFS) Open(name string) (fs.File, error) {
765765
return nil, &fs.PathError{Op: "open", Path: name, Err: fmt.Errorf("%w: %s", fs.ErrInvalid, name)}
766766
}
767767
name = path.Join(filepath.ToSlash(fsys.Root), name)
768-
realPath, innerPath := fsys.splitPath(name)
768+
realPath, innerPath := fsys.SplitPath(name)
769769
if innerPath != "" {
770770
if innerFsys := fsys.getInnerFsys(realPath); innerFsys != nil {
771771
return innerFsys.Open(innerPath)
@@ -779,7 +779,7 @@ func (fsys *DeepFS) Stat(name string) (fs.FileInfo, error) {
779779
return nil, &fs.PathError{Op: "stat", Path: name, Err: fmt.Errorf("%w: %s", fs.ErrInvalid, name)}
780780
}
781781
name = path.Join(filepath.ToSlash(fsys.Root), name)
782-
realPath, innerPath := fsys.splitPath(name)
782+
realPath, innerPath := fsys.SplitPath(name)
783783
if innerPath != "" {
784784
if innerFsys := fsys.getInnerFsys(realPath); innerFsys != nil {
785785
return fs.Stat(innerFsys, innerPath)
@@ -798,7 +798,7 @@ func (fsys *DeepFS) ReadDir(name string) ([]fs.DirEntry, error) {
798798
return nil, &fs.PathError{Op: "readdir", Path: name, Err: fmt.Errorf("%w: %s", fs.ErrInvalid, name)}
799799
}
800800
name = path.Join(filepath.ToSlash(fsys.Root), name)
801-
realPath, innerPath := fsys.splitPath(name)
801+
realPath, innerPath := fsys.SplitPath(name)
802802
if innerPath != "" {
803803
if innerFsys := fsys.getInnerFsys(realPath); innerFsys != nil {
804804
return fs.ReadDir(innerFsys, innerPath)
@@ -811,7 +811,7 @@ func (fsys *DeepFS) ReadDir(name string) ([]fs.DirEntry, error) {
811811
// make sure entries that appear to be archive files indicate they are a directory
812812
// so the fs package will try to walk them
813813
for i, entry := range entries {
814-
if slices.Contains(archiveExtensions, strings.ToLower(path.Ext(entry.Name()))) {
814+
if PathIsArchive(entry.Name()) {
815815
entries[i] = alwaysDirEntry{entry}
816816
}
817817
}
@@ -840,7 +840,7 @@ func (fsys *DeepFS) getInnerFsys(realPath string) fs.FS {
840840
return nil
841841
}
842842

843-
// splitPath splits a file path into the "real" path and the "inner" path components,
843+
// SplitPath splits a file path into the "real" path and the "inner" path components,
844844
// where the split point is the first extension of an archive filetype like ".zip" or
845845
// ".tar.gz" that occurs in the path.
846846
//
@@ -851,7 +851,7 @@ func (fsys *DeepFS) getInnerFsys(realPath string) fs.FS {
851851
// If no archive extension is found in the path, only the realPath is returned.
852852
// If the input path is precisely an archive file (i.e. ends with an archive file
853853
// extension), then innerPath is returned as "." which indicates the root of the archive.
854-
func (*DeepFS) splitPath(path string) (realPath, innerPath string) {
854+
func (*DeepFS) SplitPath(path string) (realPath, innerPath string) {
855855
if len(path) < 2 {
856856
realPath = path
857857
return
@@ -870,20 +870,20 @@ func (*DeepFS) splitPath(path string) (realPath, innerPath string) {
870870

871871
for {
872872
part := strings.TrimRight(strings.ToLower(path[start:end]), " ")
873-
for _, ext := range archiveExtensions {
874-
if strings.HasSuffix(part, ext) {
875-
// we've found an archive extension, so the path until the end of this segment is
876-
// the "real" OS path, and what remains (if anything( is the path within the archive
877-
realPath = filepath.Clean(filepath.FromSlash(path[:end]))
878-
if end < len(path) {
879-
innerPath = path[end+1:]
880-
} else {
881-
// signal to the caller that this is an archive,
882-
// even though it is the very root of the archive
883-
innerPath = "."
884-
}
885-
return
873+
if PathIsArchive(part) {
874+
// we've found an archive extension, so the path until the end of this segment is
875+
// the "real" OS path, and what remains (if anything( is the path within the archive
876+
realPath = filepath.Clean(filepath.FromSlash(path[:end]))
877+
878+
if end < len(path) {
879+
innerPath = path[end+1:]
880+
} else {
881+
// signal to the caller that this is an archive,
882+
// even though it is the very root of the archive
883+
innerPath = "."
886884
}
885+
return
886+
887887
}
888888

889889
// advance to the next segment, or end of string
@@ -936,6 +936,22 @@ var archiveExtensions = []string{
936936
".tar.lz",
937937
}
938938

939+
// PathIsArchive returns true if the path ends with an archive file (i.e.
940+
// whether the path traverse to an archive) solely by lexical analysis (no
941+
// reading the files or headers is performed).
942+
func PathIsArchive(path string) bool {
943+
// normalize the extension
944+
path = strings.ToLower(path)
945+
for _, ext := range archiveExtensions {
946+
// Check the full ext
947+
if strings.HasSuffix(path, ext) {
948+
return true
949+
}
950+
}
951+
952+
return false
953+
}
954+
939955
// PathContainsArchive returns true if the path contains an archive file (i.e.
940956
// whether the path traverses into an archive) solely by lexical analysis (no
941957
// reading of files or headers is performed). Such a path is not typically

Diff for: fs_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func TestSplitPath(t *testing.T) {
9595
expectedInner: "b/test.tar/c",
9696
},
9797
} {
98-
actualReal, actualInner := d.splitPath(testCase.input)
98+
actualReal, actualInner := d.SplitPath(testCase.input)
9999
if actualReal != testCase.expectedReal {
100100
t.Errorf("Test %d (input=%q): expected real path %q but got %q", i, testCase.input, testCase.expectedReal, actualReal)
101101
}

0 commit comments

Comments
 (0)