Skip to content

Commit

Permalink
✨ feat: fsutil - add new func IsEmptyDir() and add more unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Jul 24, 2023
1 parent 5c2e10b commit 8690507
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 97 deletions.
13 changes: 13 additions & 0 deletions fsutil/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fsutil

import (
"bytes"
"io"
"os"
"path"
"path/filepath"
Expand Down Expand Up @@ -82,6 +83,18 @@ func IsAbsPath(aPath string) bool {
return false
}

// IsEmptyDir reports whether the named directory is empty.
func IsEmptyDir(dirPath string) bool {
f, err := os.Open(dirPath)
if err != nil {
return false
}
defer f.Close()

_, err = f.Readdirnames(1)
return err == io.EOF
}

// ImageMimeTypes refer net/http package
var ImageMimeTypes = map[string]string{
"bmp": "image/bmp",
Expand Down
2 changes: 2 additions & 0 deletions fsutil/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ func TestIsDir(t *testing.T) {
assert.False(t, fsutil.IsDir("/not-exist"))
assert.True(t, fsutil.IsDir("testdata"))
assert.True(t, fsutil.DirExist("testdata"))
assert.False(t, fsutil.IsEmptyDir("testdata"))
assert.False(t, fsutil.IsEmptyDir("testdata/not-exist-dir"))
}

func TestIsAbsPath(t *testing.T) {
Expand Down
21 changes: 21 additions & 0 deletions fsutil/define_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package fsutil_test

import (
"fmt"
"sort"
"testing"
"time"

"github.com/gookit/goutil/fsutil"
"github.com/gookit/goutil/testutil/assert"
Expand All @@ -27,3 +29,22 @@ func TestNewEntry(t *testing.T) {
nfi := fsutil.NewFileInfo(fPath, fi)
assert.Equal(t, fPath, nfi.Path())
}

func TestFileInfos_Len(t *testing.T) {
fi1 := fakeobj.NewFile("/path/to/file1.txt")
fi1.Mt = time.Now()

fi2 := fakeobj.NewFile("/path/to/file2.txt")
fi2.Mt = time.Now().Add(-time.Minute)

fis := fsutil.FileInfos{
fsutil.NewFileInfo(fi1.Path, fi1),
fsutil.NewFileInfo(fi2.Path, fi2),
}

assert.Equal(t, 2, fis.Len())
assert.Eq(t, fi1.Path, fis[0].Path())

sort.Sort(fis)
assert.Eq(t, fi2.Path, fis[0].Path())
}
6 changes: 5 additions & 1 deletion fsutil/finder/matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ func MatchExts(exts []string) MatcherFunc {
// f.Not(MatchName("README.md", "*_test.go"))
func MatchName(names ...string) MatcherFunc { return MatchNames(names) }

// MatchNames match filepath by given names.
// MatchNames match filepath by given names or patterns.
//
// Usage:
//
// f.Not(MatchNames([]string{"README.md", "*_test.go"}))
func MatchNames(names []string) MatcherFunc {
return func(el Elem) bool {
elName := el.Name()
Expand Down
13 changes: 4 additions & 9 deletions fsutil/fsutil_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fsutil_test

import (
"fmt"
"io/fs"
"testing"

Expand All @@ -10,21 +11,15 @@ import (
)

func TestMain(m *testing.M) {
err := fsutil.RemoveSub("./testdata", func(fPath string, ent fs.DirEntry) bool {
return fsutil.PathMatch(ent.Name(), "*.txt")
fmt.Println("before test ... clean testdata/*.txt files")
err := fsutil.RemoveSub("testdata", func(fPath string, ent fs.DirEntry) bool {
return fsutil.PathMatch("*.txt", ent.Name())
})
basefn.MustOK(err)

m.Run()
}

func TestTempDir(t *testing.T) {
dir, err := fsutil.TempDir("testdata", "temp.*")
assert.NoErr(t, err)
assert.True(t, fsutil.IsDir(dir))
assert.NoErr(t, fsutil.Remove(dir))
}

func TestSplitPath(t *testing.T) {
dir, file := fsutil.SplitPath("/path/to/dir/some.txt")
assert.Eq(t, "/path/to/dir/", dir)
Expand Down
48 changes: 1 addition & 47 deletions fsutil/mime.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,16 @@ package fsutil

import (
"io"
"mime"
"net/http"
"os"
"strings"
)

// refer https://www.freeformatter.com/mime-types-list.html
var builtinMimeTypes = map[string]string{
".xml": "application/xml",
}

func init() {
// register builtin mime types
for ext, mimeTyp := range builtinMimeTypes {
_ = mime.AddExtensionType(ext, mimeTyp)
}
}

// ExtsByMimeType returns the extensions known to be associated with the MIME type typ.
//
// returns like: [".html"] [".jpg", ".jpeg]
func ExtsByMimeType(mimeTyp string) ([]string, error) {
return mime.ExtensionsByType(mimeTyp)
}

// ExtByMimeType returns an extension known to be associated with the MIME type typ.
//
// allow with a default ext on not found.
func ExtByMimeType(mimeTyp, defExt string) (string, error) {
ss, err := mime.ExtensionsByType(mimeTyp)
if err != nil {
if defExt != "" {
return defExt, nil
}
return "", err
}

if len(ss) == 0 {
return defExt, nil
}

// always return the best match
for _, ext := range ss {
if strings.Index(mimeTyp, ext[1:]) > 0 {
return ext, nil
}
}
return ss[0], nil
}

// DetectMime detect file mime type. alias of MimeType()
func DetectMime(path string) string {
return MimeType(path)
}

// MimeType get File Mime Type name. eg "image/png"
// MimeType get file mime type name. eg "image/png"
func MimeType(path string) (mime string) {
file, err := os.Open(path)
if err != nil {
Expand Down
38 changes: 0 additions & 38 deletions fsutil/mime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,6 @@ import (
"github.com/gookit/goutil/testutil/assert"
)

func TestExtByMimeType(t *testing.T) {
tests := []struct {
mime string
ok bool
exts []string
ext string
}{
{"application/json", true, []string{".json"}, ".json"},
{"application/xml", true, []string{".xml"}, ".xml"},
{"application/x-yaml", true, []string{".yaml", ".yml"}, ".yaml"},
{"text/plain; charset=utf-8", true, []string{".asc", ".txt"}, ".txt"},
}

for _, tt := range tests {
es, err := fsutil.ExtsByMimeType(tt.mime)
if tt.ok {
assert.NoErr(t, err)
assert.Eq(t, tt.exts, es)
} else {
assert.Err(t, err)
assert.Empty(t, es)
}

ext, err := fsutil.ExtByMimeType(tt.mime, "")
if tt.ok {
assert.NoErr(t, err)
assert.Eq(t, tt.ext, ext)
} else {
assert.Err(t, err)
assert.Empty(t, ext)
}
}

ext, err := fsutil.ExtByMimeType("application/not-exists", "default")
assert.NoErr(t, err)
assert.Eq(t, "default", ext)
}

func TestMimeType(t *testing.T) {
assert.Eq(t, "", fsutil.DetectMime(""))
assert.Eq(t, "", fsutil.MimeType("not-exist"))
Expand Down
7 changes: 6 additions & 1 deletion fsutil/operate.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func MkSubDirs(perm os.FileMode, parentDir string, subDirs ...string) error {
return nil
}

// MkParentDir quick create parent dir
// MkParentDir quick create parent dir for given path.
func MkParentDir(fpath string) error {
dirPath := filepath.Dir(fpath)
if !IsDir(dirPath) {
Expand Down Expand Up @@ -262,6 +262,11 @@ func RemoveSub(dirPath string, fns ...FilterFunc) error {
if err := RemoveSub(fPath, fns...); err != nil {
return err
}

// skip rm not empty subdir
if !IsEmptyDir(fPath) {
return nil
}
}
return os.Remove(fPath)
}, fns...)
Expand Down
5 changes: 4 additions & 1 deletion fsutil/opwrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ func OSTempFile(pattern string) (*os.File, error) {
//
// Usage:
//
// // create temp file on os.TempDir()
// fsutil.TempFile("", "example.*.txt")
// // create temp file on "testdata" dir
// fsutil.TempFile("testdata", "example.*.txt")
func TempFile(dir, pattern string) (*os.File, error) {
return os.CreateTemp(dir, pattern)
}
Expand Down Expand Up @@ -158,7 +161,7 @@ func MustCopyFile(srcPath, dstPath string) {

// UpdateContents read file contents, call handleFn(contents) handle, then write updated contents to file
func UpdateContents(filePath string, handleFn func(bs []byte) []byte) error {
osFile, err := os.OpenFile(filePath, os.O_RDWR, 0600)
osFile, err := os.OpenFile(filePath, os.O_RDWR|os.O_TRUNC, 0600)
if err != nil {
return err
}
Expand Down
35 changes: 35 additions & 0 deletions fsutil/opwrite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fsutil_test
import (
"testing"

"github.com/gookit/goutil/dump"
"github.com/gookit/goutil/fsutil"
"github.com/gookit/goutil/testutil/assert"
)
Expand Down Expand Up @@ -70,4 +71,38 @@ func TestUpdateContents(t *testing.T) {
err := fsutil.UpdateContents("testdata/not-exists-file", nil)
assert.Err(t, err)

of, err := fsutil.TempFile("testdata", "test-update-contents-*.txt")
assert.NoErr(t, err)

dump.P(of.Name())
_, err = of.WriteString("hello")
assert.NoErr(t, err)
assert.NoErr(t, of.Close())

err = fsutil.UpdateContents(of.Name(), func(bs []byte) []byte {
return []byte("hello, world")
})
assert.NoErr(t, err)
assert.Eq(t, "hello, world", fsutil.ReadString(of.Name()))
}

func TestOSTempFile(t *testing.T) {
of, err := fsutil.OSTempFile("test-os-tmpfile-*.txt")
assert.NoErr(t, err)
defer of.Close()

dump.P(of.Name())
assert.StrContains(t, of.Name(), "test-os-tmpfile-")
}

func TestTempDir(t *testing.T) {
dir, err := fsutil.TempDir("testdata", "temp-dir-*")
assert.NoErr(t, err)
assert.True(t, fsutil.IsDir(dir))
assert.NoErr(t, fsutil.Remove(dir))

dir, err = fsutil.OSTempDir("os-temp-dir-*")
assert.NoErr(t, err)
assert.True(t, fsutil.IsDir(dir))
assert.True(t, fsutil.IsEmptyDir(dir))
}

0 comments on commit 8690507

Please sign in to comment.