diff --git a/.travis.yml b/.travis.yml index 66c8fe0..c7285ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,14 +4,13 @@ os: - linux go: - - 1.5 - - 1.6 - 1.7 - 1.8 - 1.9 - "1.10" - 1.11 - 1.12 + - 1.13 install: - make setup diff --git a/Gopkg.lock b/Gopkg.lock index c28496d..aa9fbcb 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1,23 +1,29 @@ # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. +[[projects]] + name = "github.com/JaSei/hashutil-go" + packages = ["."] + revision = "3ff95654e7b708d1e549d0fc8c58596060846656" + version = "2.0.0" + [[projects]] name = "github.com/davecgh/go-spew" packages = ["spew"] - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" [[projects]] name = "github.com/mitchellh/go-homedir" packages = ["."] - revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4" - version = "v1.0.0" + revision = "af06845cf3004701891bf4fdb884bfe4920b3727" + version = "v1.1.0" [[projects]] name = "github.com/pkg/errors" packages = ["."] - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" + revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" + version = "v0.8.1" [[projects]] name = "github.com/pmezard/go-difflib" @@ -28,12 +34,18 @@ [[projects]] name = "github.com/stretchr/testify" packages = ["assert"] - revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0" - version = "v1.1.4" + revision = "221dbe5ed46703ee255b1da0dec05086f5035f62" + version = "v1.4.0" + +[[projects]] + name = "gopkg.in/yaml.v2" + packages = ["."] + revision = "f90ceb4f409096b60e2e9076b38b304b8246e5fa" + version = "v2.2.5" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "e1bad7702ae86fa3c5e9348f2bea7b41bde7c618fc895b9c4b9a88c89b6cce29" + inputs-digest = "c840c37d3244f4c44699b96ac39cbfa8d0a87bdf3b200ac9d54b3012fe5e3f0b" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index ea43025..20fcb0e 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -9,3 +9,7 @@ [[constraint]] name = "github.com/mitchellh/go-homedir" version = "1.0.0" + +[[constraint]] + name = "github.com/JaSei/hashutil-go" + version = "2.0.0" diff --git a/Makefile b/Makefile index f3a377b..1b520af 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,5 @@ HELP?=$$(go run main.go --help 2>&1) VERSION?=$$(cat VERSION) -GONEWER?=$(shell go version | grep -E "go1\.1[01]") DEP?=$$(which dep) LINTER?=$$(which golangci-lint) LINTER_VERSION=1.15.0 @@ -28,7 +27,7 @@ setup: ## Install all the build and lint dependencies curl -L https://github.com/golang/dep/releases/download/v0.5.1/$(DEP_VERS) >| $$GOPATH/bin/dep;\ chmod +x $$GOPATH/bin/dep;\ fi - dep ensure + dep ensure -v @if [ "$(LINTER)" = "" ]; then\ curl -L https://github.com/golangci/golangci-lint/releases/download/v$(LINTER_VERSION)/$(LINTER_FILE) $(LINTER_UNPACK) ;\ diff --git a/README.md b/README.md index 6499348..938c1dc 100644 --- a/README.md +++ b/README.md @@ -53,30 +53,75 @@ functions which isn't in core libraries (like `Copy` for example) * [pathlib](https://docs.python.org/3/library/pathlib.html) for python -## Usage +BREAKING CHANGE 0.3.1 -> 1.0.0 -#### type CryptoHash +1. `NewTempFile` or `NewTempDir` don't need TempOpt struct -```go -type CryptoHash struct { - hash.Hash -} -``` + //0.3.1 default + pathutil.NewTempFile(pathutil.TempOpt{}) + //0.3.1 custom + pathutil.NewTempFile(pathutil.TempOpt{Dir: "/test", Prefix: "pre"}) + //1.0.0 default + pathutil.NewTempFile() + //1.0.0 custom + pathutil.NewTempFile(Dir("/test"), Prefix("pre")) -#### func (*CryptoHash) BinSum +2. `New` method parameter allowed `string` type or type implements +`fmt.Stringer` interface -```go -func (hash *CryptoHash) BinSum() []byte -``` -BinSum method is like hash.Sum(nil) + //0.3.1 + pathutil.New(otherPath.String(), "test") -#### func (*CryptoHash) HexSum + //1.0.0 + pathutil.New(otherPath, "test") -```go -func (hash *CryptoHash) HexSum() string -``` -HexSum method retun hexstring representation of hash.Sum +This shouldn't be breaking change, but if you use in some code variadic +parameter as input of `pathutil.New`, then can be problem + + //0.3.1 + func(p ...string) { + pathutil.New(p...) + }("a", "b") + + //1.0.0 + func(p ...string) { + n := make([]interface{}, len(p)) + for i, v := range p { + n[i] = v + } + pathutil.New(n...) + }("a", "b") + +3. There is new (more handfull) crypto API + + //0.3.1 + import ( + "crypto" + "github.com/JaSei/pathutil-go" + ) + ... + + hash, err := path.Crypto(crypto.SHA256) + if err == nil { + fmt.Printf("%s\t%s\n", hash.HexSum(), path.String()) + } + + //1.0.0 + import ( + "github.com/JaSei/pathutil-go" + ) + ... + + hash, err := path.CryptoSha256() + if err == nil { + fmt.Printf("%s\t%s\n", hash, path.String()) + } + +This new crypto API return [hashutil](github.com/JaSei/hashutil-go) struct which +is more handfull for compare, transformation and next hash manipulation. + +## Usage #### type LinesFunc @@ -111,7 +156,11 @@ type Path interface { CopyFrom(io.Reader) (int64, error) - Crypto(crypto.Hash) (*CryptoHash, error) + CryptoMd5() (hashutil.Md5, error) + CryptoSha1() (hashutil.Sha1, error) + CryptoSha256() (hashutil.Sha256, error) + CryptoSha384() (hashutil.Sha384, error) + CryptoSha512() (hashutil.Sha512, error) MakePath() error MakePathMode(os.FileMode) error @@ -168,7 +217,7 @@ for example #### func New ```go -func New(path ...string) (Path, error) +func New(path ...interface{}) (Path, error) ``` New construct Path @@ -176,24 +225,29 @@ for example path := New("/home/test", ".vimrc") -if you can use `Path` in `New`, you must use `.String()` method +input can be `string` or type implements `fmt.Stringer` interface #### func NewTempDir ```go -func NewTempDir(options TempOpt) (Path, error) +func NewTempDir(options ...TempOpt) (Path, error) ``` NewTempDir create temp directory for cleanup use `defer` - tempdir, err := pathutil.NewTempDir(TempOpt{}) + tempdir, err := pathutil.NewTempDir() defer tempdir.RemoveTree() +if you need set directory or prefix, then use `TempDir` and/or `TempPrefix` + + temp, err := NewTempFile(TempDir("/home/my/test"), TempPrefix("myfile")) + ... + #### func NewTempFile ```go -func NewTempFile(options TempOpt) (p Path, err error) +func NewTempFile(options ...TempOpt) (p Path, err error) ``` NewTempFile create temp file @@ -204,9 +258,14 @@ for cleanup use defer if you need only temp file name, you must delete file - temp, err := NewTempFile(TempFileOpt{}) + temp, err := NewTempFile() temp.Remove() +if you need set directory or prefix, then use `TempDir` and/or `TempPrefix` + + temp, err := NewTempFile(TempDir("/home/my/test"), TempPrefix("myfile")) + ... + #### type PathImpl ```go @@ -275,11 +334,70 @@ func (path PathImpl) CopyFrom(reader io.Reader) (copyied int64, err error) ``` CopyFrom copy stream from reader to path (file after copy close and sync) -#### func (PathImpl) Crypto +#### func (PathImpl) CryptoMd5 + +```go +func (path PathImpl) CryptoMd5() (hashutil.Md5, error) +``` +CryptoMd5 method access hash funcionality like Path::Tiny Digest return +[hashutil.Md5](github.com/JaSei/hashutil-go) type + +for example print of Md5 hexstring + + hash, err := path.CryptoMd5() + fmt.Println(hash) + +#### func (PathImpl) CryptoSha1 ```go -func (path PathImpl) Crypto(hash crypto.Hash) (c *CryptoHash, err error) +func (path PathImpl) CryptoSha1() (hashutil.Sha1, error) ``` +CryptoSha1 method access hash funcionality like Path::Tiny Digest return +[hashutil.Sha1](github.com/JaSei/hashutil-go) type + +for example print of Sha1 hexstring + + hash, err := path.CryptoSha1() + fmt.Println(hash) + +#### func (PathImpl) CryptoSha256 + +```go +func (path PathImpl) CryptoSha256() (hashutil.Sha256, error) +``` +CryptoSha256 method access hash funcionality like Path::Tiny Digest return +[hashutil.Sha256](github.com/JaSei/hashutil-go) type + +for example print of Sha256 hexstring + + hash, err := path.CryptoSha256() + fmt.Println(hash) + +#### func (PathImpl) CryptoSha384 + +```go +func (path PathImpl) CryptoSha384() (hashutil.Sha384, error) +``` +CryptoSha384 method access hash funcionality like Path::Tiny Digest return +[hashutil.Sha384](github.com/JaSei/hashutil-go) type + +for example print of Sha284 hexstring + + hash, err := path.CryptoSha284() + fmt.Println(hash) + +#### func (PathImpl) CryptoSha512 + +```go +func (path PathImpl) CryptoSha512() (hashutil.Sha512, error) +``` +CryptoSha512 method access hash funcionality like Path::Tiny Digest return +[hashutil.Sha512](github.com/JaSei/hashutil-go) type + +for example print of Sha512 hexstring + + hash, err := path.CryptoSha512() + fmt.Println(hash) #### func (PathImpl) Exists @@ -493,15 +611,25 @@ type ReadSeekCloser interface { #### type TempOpt ```go -type TempOpt struct { - // directory where is temp file/dir create, empty string `""` (default) means TEMPDIR (`os.TempDir`) - Dir string - // name beginning with prefix - Prefix string -} +type TempOpt func(*tempOpt) ``` -TempOpt is struct for configure new tempdir or tempfile +TempOpt is func for configure tempdir or tempfile + +#### func TempDir + +```go +func TempDir(dir string) TempOpt +``` +TempDir set directory where is temp file/dir create, empty string `""` (default) +means TEMPDIR (`os.TempDir`) + +#### func TempPrefix + +```go +func TempPrefix(prefix string) TempOpt +``` +TempPrefix set name beginning with prefix #### type VisitFunc diff --git a/VERSION b/VERSION index 9e11b32..3eefcb9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.1 +1.0.0 diff --git a/append_test.go b/append_test.go index d9aa7b5..fbca5ad 100644 --- a/append_test.go +++ b/append_test.go @@ -7,7 +7,7 @@ import ( ) func TestAppend(t *testing.T) { - temp, err := NewTempFile(TempOpt{}) + temp, err := NewTempFile() assert.NoError(t, err) assert.NoError(t, temp.Append("test\n")) diff --git a/base_test.go b/base_test.go index b6cfdce..65b7505 100644 --- a/base_test.go +++ b/base_test.go @@ -3,6 +3,7 @@ package pathutil import ( "fmt" "path/filepath" + "runtime" "testing" "github.com/stretchr/testify/assert" @@ -30,7 +31,12 @@ func TestCanonpath(t *testing.T) { assert.Equal(t, fmt.Sprintf("tmp%stest", string(filepath.Separator)), path.Canonpath(), "more paths elemets") path, _ = New("tmp\\test") - assert.Equal(t, fmt.Sprintf("tmp%stest", string(filepath.Separator)), path.Canonpath(), "windows rel path") + assert.Equal(t, "tmp\\test", path.Canonpath(), "windows rel path") + if runtime.GOOS == "windows" { + assert.Equal(t, "tmp/test", path.String(), "windows backshlash are internaly represents as slash") + } else { + assert.Equal(t, "tmp\\test", path.String(), "backshlash in linux path is allowed") + } } func TestBasename(t *testing.T) { diff --git a/chdir_test.go b/chdir_test.go index b705632..0e0d97c 100644 --- a/chdir_test.go +++ b/chdir_test.go @@ -7,7 +7,7 @@ import ( ) func TestChdir(t *testing.T) { - tempdir, err := NewTempDir(TempOpt{}) + tempdir, err := NewTempDir() assert.NoError(t, err) cwd, err := Cwd() diff --git a/child.go b/child.go index 2ed660a..78a0632 100644 --- a/child.go +++ b/child.go @@ -5,9 +5,13 @@ import ( ) func (path PathImpl) Child(childName ...string) (Path, error) { - pathChunks := append([]string{path.String()}, childName...) + p := make([]interface{}, len(childName)+1) + p[0] = path + for i, c := range childName { + p[i+1] = c + } - return New(pathChunks...) + return New(p...) } func (path PathImpl) Children() ([]Path, error) { diff --git a/child_test.go b/child_test.go index 575c13e..f8885bd 100644 --- a/child_test.go +++ b/child_test.go @@ -7,7 +7,7 @@ import ( ) func TestChild(t *testing.T) { - tempdir, err := NewTempDir(TempOpt{}) + tempdir, err := NewTempDir() assert.NoError(t, err) defer func() { @@ -22,7 +22,7 @@ func TestChild(t *testing.T) { } func TestChildren(t *testing.T) { - tempdir, err := NewTempDir(TempOpt{}) + tempdir, err := NewTempDir() assert.NoError(t, err) defer func() { diff --git a/copy_test.go b/copy_test.go index b2203b7..71130df 100644 --- a/copy_test.go +++ b/copy_test.go @@ -9,7 +9,7 @@ import ( func TestCopyFileDstFileExists(t *testing.T) { src, _ := New("copy_test.go") - dst, err := NewTempFile(TempOpt{}) + dst, err := NewTempFile() assert.Nil(t, err) defer func() { assert.NoError(t, dst.Remove()) diff --git a/crypto.go b/crypto.go index 9cdd086..97adb57 100644 --- a/crypto.go +++ b/crypto.go @@ -1,20 +1,92 @@ package pathutil import ( - "crypto" - "fmt" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" "hash" "io" + + "github.com/JaSei/hashutil-go" ) -// Crypto method access hash funcionality like Path::Tiny Digest -// look to https://godoc.org/crypto#Hash for list possible crypto hash functions +// CryptoMd5 method access hash funcionality like Path::Tiny Digest +// return [hashutil.Md5](github.com/JaSei/hashutil-go) type +// +// for example print of Md5 hexstring +// hash, err := path.CryptoMd5() +// fmt.Println(hash) +func (path PathImpl) CryptoMd5() (hashutil.Md5, error) { + c, err := path.crypto(md5.New()) + if err != nil { + return hashutil.Md5{}, nil + } + + return hashutil.HashToMd5(c) +} + +// CryptoSha1 method access hash funcionality like Path::Tiny Digest +// return [hashutil.Sha1](github.com/JaSei/hashutil-go) type +// +// for example print of Sha1 hexstring +// hash, err := path.CryptoSha1() +// fmt.Println(hash) +func (path PathImpl) CryptoSha1() (hashutil.Sha1, error) { + c, err := path.crypto(sha1.New()) + if err != nil { + return hashutil.Sha1{}, nil + } + + return hashutil.HashToSha1(c) +} + +// CryptoSha256 method access hash funcionality like Path::Tiny Digest +// return [hashutil.Sha256](github.com/JaSei/hashutil-go) type // // for example print of Sha256 hexstring -// hash, err := path.Crypto(crypto.SHA256) -// fmt.Println(hash.HexSum()) +// hash, err := path.CryptoSha256() +// fmt.Println(hash) +func (path PathImpl) CryptoSha256() (hashutil.Sha256, error) { + c, err := path.crypto(sha256.New()) + if err != nil { + return hashutil.Sha256{}, nil + } -func (path PathImpl) Crypto(hash crypto.Hash) (c *CryptoHash, err error) { + return hashutil.HashToSha256(c) +} + +// CryptoSha384 method access hash funcionality like Path::Tiny Digest +// return [hashutil.Sha384](github.com/JaSei/hashutil-go) type +// +// for example print of Sha284 hexstring +// hash, err := path.CryptoSha284() +// fmt.Println(hash) +func (path PathImpl) CryptoSha384() (hashutil.Sha384, error) { + c, err := path.crypto(sha512.New384()) + if err != nil { + return hashutil.Sha384{}, nil + } + + return hashutil.HashToSha384(c) +} + +// CryptoSha512 method access hash funcionality like Path::Tiny Digest +// return [hashutil.Sha512](github.com/JaSei/hashutil-go) type +// +// for example print of Sha512 hexstring +// hash, err := path.CryptoSha512() +// fmt.Println(hash) +func (path PathImpl) CryptoSha512() (hashutil.Sha512, error) { + c, err := path.crypto(sha512.New()) + if err != nil { + return hashutil.Sha512{}, nil + } + + return hashutil.HashToSha512(c) +} + +func (path PathImpl) crypto(h hash.Hash) (ret hash.Hash, err error) { reader, err := path.OpenReader() if err != nil { return nil, err @@ -25,30 +97,11 @@ func (path PathImpl) Crypto(hash crypto.Hash) (c *CryptoHash, err error) { } }() - h := hash.New() - _, err = io.Copy(h, reader) if err != nil { return nil, err } - return &CryptoHash{h}, nil -} - -// CryptoHash struct is only abstract for hash.Hash interface -// for possible use with methods - -type CryptoHash struct { - hash.Hash -} - -// BinSum method is like hash.Sum(nil) -func (hash *CryptoHash) BinSum() []byte { - return hash.Sum(nil) -} - -// HexSum method retun hexstring representation of hash.Sum -func (hash *CryptoHash) HexSum() string { - return fmt.Sprintf("%x", hash.Sum(nil)) + return h, nil } diff --git a/crypto_test.go b/crypto_test.go index 6240b1a..04bae20 100644 --- a/crypto_test.go +++ b/crypto_test.go @@ -1,26 +1,36 @@ package pathutil import ( - "crypto" "testing" + "github.com/JaSei/hashutil-go" "github.com/stretchr/testify/assert" ) -const emptyFileSha256Hex = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - -var emptyFileSha256Bin = []uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55} - func TestPathCrypto(t *testing.T) { - path, err := NewTempFile(TempOpt{}) + path, err := NewTempFile() assert.Nil(t, err) defer func() { assert.NoError(t, path.Remove()) }() - hash, err := path.Crypto(crypto.Hash(crypto.SHA256)) + md5Hash, err := path.CryptoMd5() assert.NoError(t, err) + assert.True(t, hashutil.EmptyMd5().Equal(md5Hash)) - assert.Equal(t, emptyFileSha256Hex, hash.HexSum()) - assert.Equal(t, emptyFileSha256Bin, hash.BinSum()) + sha1Hash, err := path.CryptoSha1() + assert.NoError(t, err) + assert.True(t, hashutil.EmptySha1().Equal(sha1Hash)) + + sha256Hash, err := path.CryptoSha256() + assert.NoError(t, err) + assert.True(t, hashutil.EmptySha256().Equal(sha256Hash)) + + sha384Hash, err := path.CryptoSha384() + assert.NoError(t, err) + assert.True(t, hashutil.EmptySha384().Equal(sha384Hash)) + + sha512Hash, err := path.CryptoSha512() + assert.NoError(t, err) + assert.True(t, hashutil.EmptySha512().Equal(sha512Hash)) } diff --git a/doc.go b/doc.go index b6f67ba..eacbe18 100644 --- a/doc.go +++ b/doc.go @@ -41,5 +41,71 @@ SEE ALSO * [pathlib](https://docs.python.org/3/library/pathlib.html) for python +BREAKING CHANGE 0.3.1 -> 1.0.0 + +1. `NewTempFile` or `NewTempDir` don't need TempOpt struct + + //0.3.1 default + pathutil.NewTempFile(pathutil.TempOpt{}) + //0.3.1 custom + pathutil.NewTempFile(pathutil.TempOpt{Dir: "/test", Prefix: "pre"}) + + //1.0.0 default + pathutil.NewTempFile() + //1.0.0 custom + pathutil.NewTempFile(Dir("/test"), Prefix("pre")) + +2. `New` method parameter allowed `string` type or type implements `fmt.Stringer` interface + + //0.3.1 + pathutil.New(otherPath.String(), "test") + + //1.0.0 + pathutil.New(otherPath, "test") + +This shouldn't be breaking change, but if you use in some code variadic parameter as input of `pathutil.New`, then can be problem + + //0.3.1 + func(p ...string) { + pathutil.New(p...) + }("a", "b") + + //1.0.0 + func(p ...string) { + n := make([]interface{}, len(p)) + for i, v := range p { + n[i] = v + } + pathutil.New(n...) + }("a", "b") + +3. There is new (more handfull) crypto API + + //0.3.1 + import ( + "crypto" + "github.com/JaSei/pathutil-go" + ) + ... + + hash, err := path.Crypto(crypto.SHA256) + if err == nil { + fmt.Printf("%s\t%s\n", hash.HexSum(), path.String()) + } + + //1.0.0 + import ( + "github.com/JaSei/pathutil-go" + ) + ... + + hash, err := path.CryptoSha256() + if err == nil { + fmt.Printf("%s\t%s\n", hash, path.String()) + } + +This new crypto API return [hashutil](github.com/JaSei/hashutil-go) struct +which is more handfull for compare, transformation and next hash manipulation. + */ package pathutil diff --git a/examples/download_test.go b/examples/download_test.go index 2ad48e2..ecc50fa 100644 --- a/examples/download_test.go +++ b/examples/download_test.go @@ -1,6 +1,7 @@ package pathutil_test import ( + "fmt" "net/http" "testing" @@ -8,6 +9,8 @@ import ( "github.com/stretchr/testify/assert" ) +const sizeOfExampleSite = 1256 + func TestDownload(t *testing.T) { response, err := http.Get("http://example.com") @@ -19,7 +22,7 @@ func TestDownload(t *testing.T) { assert.NoError(t, response.Body.Close()) }() - temp, err := pathutil.NewTempFile(pathutil.TempOpt{}) + temp, err := pathutil.NewTempFile() defer func() { assert.NoError(t, temp.Remove()) @@ -33,11 +36,11 @@ func TestDownload(t *testing.T) { t.Log(err) } - assert.Equal(t, int64(1270), copyied, "Copy 1270 bytes") + assert.Equal(t, int64(sizeOfExampleSite), copyied, fmt.Sprintf("Copy %d bytes", sizeOfExampleSite)) ctx, err := temp.Slurp() assert.Nil(t, err) - assert.Equal(t, 1270, len(ctx), "Size of http://example.com are 1270") + assert.Equal(t, sizeOfExampleSite, len(ctx), fmt.Sprintf("Size of http://example.com are %d", sizeOfExampleSite)) } diff --git a/examples/json_test.go b/examples/json_test.go index 8e800ba..92c6c04 100644 --- a/examples/json_test.go +++ b/examples/json_test.go @@ -14,12 +14,12 @@ type FileSource struct { } type FileInfo struct { - FileId string `json:"file_id"` + FileID string `json:"file_id"` Sources []FileSource `json:"sources"` } var expected = FileInfo{ - FileId: "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b", + FileID: "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b", Sources: []FileSource{ {Path: "c:\\tmp\\empty_file", Size: 0}, {Path: "/tmp/empty_file", Size: 0}, @@ -37,14 +37,14 @@ func TestLoadJsonViaReader(t *testing.T) { }() assert.NotNil(t, reader) - decodedJson := new(FileInfo) + decodedJSON := new(FileInfo) - err = json.NewDecoder(reader).Decode(decodedJson) + err = json.NewDecoder(reader).Decode(decodedJSON) if !assert.Nil(t, err) { t.Log(err) } - assert.Equal(t, &expected, decodedJson) + assert.Equal(t, &expected, decodedJSON) } func TestLoadJsonViaSlurp(t *testing.T) { @@ -54,9 +54,9 @@ func TestLoadJsonViaSlurp(t *testing.T) { jsonBytes, err := path.SlurpBytes() assert.Nil(t, err) - decodedJson := new(FileInfo) - assert.NoError(t, json.Unmarshal(jsonBytes, decodedJson)) + decodedJSON := new(FileInfo) + assert.NoError(t, json.Unmarshal(jsonBytes, decodedJSON)) - assert.Equal(t, &expected, decodedJson) + assert.Equal(t, &expected, decodedJSON) } diff --git a/examples/recursive_sha256sum_test.go b/examples/recursive_sha256sum_test.go index 587829d..cfa81b3 100644 --- a/examples/recursive_sha256sum_test.go +++ b/examples/recursive_sha256sum_test.go @@ -1,7 +1,6 @@ package pathutil_test import ( - "crypto" "fmt" "testing" @@ -19,10 +18,10 @@ func TestVisitRecursiveAndHashAllFiles(t *testing.T) { return } - hash, err := path.Crypto(crypto.SHA256) + hash, err := path.CryptoSha256() if err == nil { - fmt.Printf("%s\t%s\n", hash.HexSum(), path.String()) + fmt.Printf("%s\t%s\n", hash, path) } else { fmt.Println(err) } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..31e84c9 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module github.com/JaSei/pathutil-go + +go 1.13 + +require ( + github.com/JaSei/hashutil-go v0.0.0-20191113002909-3ff95654e7b7 + github.com/davecgh/go-spew v1.1.0 + github.com/mitchellh/go-homedir v1.1.0 + github.com/pkg/errors v0.8.1 + github.com/pmezard/go-difflib v1.0.0 + github.com/stretchr/testify v1.4.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..01031fa --- /dev/null +++ b/go.sum @@ -0,0 +1,34 @@ +github.com/JaSei/hashutil-go v0.0.0-20191112215904-68c1143bd752 h1:6BkUFNb7MD2xBjMRmsK+05yH7fv7SXKDjJoBcdKwfLc= +github.com/JaSei/hashutil-go v0.0.0-20191112215904-68c1143bd752/go.mod h1:KfUdGepj7NfgNi71ag8R/Rg0P9vVFvinLZ/HV+8lcbM= +github.com/JaSei/hashutil-go v0.0.0-20191113002909-3ff95654e7b7 h1:kAn8WH50nInf59BKMYRUuJdAS2bszTd/bc66Kmyuxp4= +github.com/JaSei/hashutil-go v0.0.0-20191113002909-3ff95654e7b7/go.mod h1:KfUdGepj7NfgNi71ag8R/Rg0P9vVFvinLZ/HV+8lcbM= +github.com/JaSei/hashutil-go v2.0.0+incompatible/go.mod h1:KfUdGepj7NfgNi71ag8R/Rg0P9vVFvinLZ/HV+8lcbM= +github.com/JaSei/hashutil-go/v2 v2.0.0 h1:hlvFwd9n4YbrD1nQvUCVV8oHiWdN70QjsSrf72R4vcU= +github.com/JaSei/hashutil-go/v2 v2.0.0/go.mod h1:Ax2z7igPgM4ZCQwgF02LlXJD7g9U5T0cD90BlpbTm0k= +github.com/JaSei/pathutil-go v0.3.1/go.mod h1:275Ib+wBbNQuFoN9w960OGHEc7dDf4dV9SCUIe+FoBA= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/pierrre/gotestcover v0.0.0-20160517101806-924dca7d15f0/go.mod h1:4xpMLz7RBWyB+ElzHu8Llua96TRCB3YwX+l5EP1wmHk= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20191111134443-9f4e7946603e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/interface.go b/interface.go index 08eea3d..56ac3de 100644 --- a/interface.go +++ b/interface.go @@ -1,9 +1,10 @@ package pathutil import ( - "crypto" "io" "os" + + "github.com/JaSei/hashutil-go" ) type VisitFunc func(path Path) @@ -37,7 +38,11 @@ type Path interface { CopyFrom(io.Reader) (int64, error) - Crypto(crypto.Hash) (*CryptoHash, error) + CryptoMd5() (hashutil.Md5, error) + CryptoSha1() (hashutil.Sha1, error) + CryptoSha256() (hashutil.Sha256, error) + CryptoSha384() (hashutil.Sha384, error) + CryptoSha512() (hashutil.Sha512, error) MakePath() error MakePathMode(os.FileMode) error diff --git a/makepath_test.go b/makepath_test.go index 7940367..e8b5390 100644 --- a/makepath_test.go +++ b/makepath_test.go @@ -7,14 +7,14 @@ import ( ) func TestMakePath(t *testing.T) { - tempdir, err := NewTempDir(TempOpt{}) + tempdir, err := NewTempDir() assert.NoError(t, err) defer func() { assert.NoError(t, tempdir.RemoveTree()) }() - newPath, err := New(tempdir.String(), "a/b/c") + newPath, err := New(tempdir, "a/b/c") assert.NoError(t, err) assert.False(t, newPath.Exists()) diff --git a/new.go b/new.go index a213888..e4c1ed5 100644 --- a/new.go +++ b/new.go @@ -1,9 +1,11 @@ package pathutil import ( + "fmt" "io/ioutil" "os" "path/filepath" + "runtime" "strings" "github.com/mitchellh/go-homedir" @@ -16,30 +18,59 @@ import ( // path := New("/home/test", ".vimrc") // // -// if you can use `Path` in `New`, you must use `.String()` method -func New(path ...string) (Path, error) { +// input can be `string` or type implements `fmt.Stringer` interface +func New(path ...interface{}) (Path, error) { newPath := PathImpl{} - for index, pathChunk := range path { + paths := make([]string, len(path)) + for index, chunk := range path { + var pathChunk string + switch t := chunk.(type) { + case string: + pathChunk = chunk.(string) + case fmt.Stringer: + pathChunk = chunk.(fmt.Stringer).String() + default: + return nil, errors.Errorf("Chunk %d is type %t (allowed are string or fmt.Stringer)", index, t) + } + if len(pathChunk) == 0 { return nil, errors.Errorf("Paths requires defined, positive-lengths parts (part %d is empty", index) } + + paths[index] = pathChunk } - joinPath := filepath.Join(path...) + joinPath := filepath.Join(paths...) + newPath.path = filepath.Clean(joinPath) - newPath.path = strings.Replace(filepath.Clean(joinPath), "\\", "/", -1) + if runtime.GOOS == "windows" { + newPath.path = strings.Replace(newPath.path, "\\", "/", -1) + } return newPath, nil } -//TempOpt is struct for configure new tempdir or tempfile -type TempOpt struct { - // directory where is temp file/dir create, empty string `""` (default) means TEMPDIR (`os.TempDir`) - Dir string - // name beginning with prefix - // if prefix includes a "*", the random string replaces the last "*". - Prefix string +type tempOpt struct { + dir string + prefix string +} + +//TempOpt is func for configure tempdir or tempfile +type TempOpt func(*tempOpt) + +// TempDir set directory where is temp file/dir create, empty string `""` (default) means TEMPDIR (`os.TempDir`) +func TempDir(dir string) TempOpt { + return func(o *tempOpt) { + o.dir = dir + } +} + +// TempPrefix set name beginning with prefix +func TempPrefix(prefix string) TempOpt { + return func(o *tempOpt) { + o.prefix = prefix + } } // NewTempFile create temp file @@ -49,13 +80,22 @@ type TempOpt struct { // defer temp.Remove() // // if you need only temp file name, you must delete file -// temp, err := NewTempFile(TempFileOpt{}) +// temp, err := NewTempFile() // temp.Remove() // -func NewTempFile(options TempOpt) (p Path, err error) { - file, err := tempFile(options.Dir, options.Prefix) +// if you need set directory or prefix, then use `TempDir` and/or `TempPrefix` +// temp, err := NewTempFile(TempDir("/home/my/test"), TempPrefix("myfile")) +// ... +// +func NewTempFile(options ...TempOpt) (p Path, err error) { + opt := tempOpt{} + for _, o := range options { + o(&opt) + } + + file, err := tempFile(opt.dir, opt.prefix) if err != nil { - return nil, errors.Wrapf(err, "NewTempFile(%+v) fail", options) + return nil, errors.Wrapf(err, "NewTempFile(%+v) fail", opt) } defer func() { @@ -70,12 +110,21 @@ func NewTempFile(options TempOpt) (p Path, err error) { // NewTempDir create temp directory // // for cleanup use `defer` -// tempdir, err := pathutil.NewTempDir(TempOpt{}) +// tempdir, err := pathutil.NewTempDir() // defer tempdir.RemoveTree() -func NewTempDir(options TempOpt) (Path, error) { - dir, err := ioutil.TempDir(options.Dir, options.Prefix) +// +// if you need set directory or prefix, then use `TempDir` and/or `TempPrefix` +// temp, err := NewTempFile(TempDir("/home/my/test"), TempPrefix("myfile")) +// ... +func NewTempDir(options ...TempOpt) (Path, error) { + opt := tempOpt{} + for _, o := range options { + o(&opt) + } + + dir, err := ioutil.TempDir(opt.dir, opt.prefix) if err != nil { - return nil, errors.Wrapf(err, "NewTempDir(%+v) fail", options) + return nil, errors.Wrapf(err, "NewTempDir(%+v) fail", opt) } return New(dir) @@ -112,8 +161,8 @@ func Home(subpath ...string) (Path, error) { return New(join(home, subpath)...) } -func join(a string, b []string) []string { - p := make([]string, len(b)+1) +func join(a string, b []string) []interface{} { + p := make([]interface{}, len(b)+1) p[0] = a for i, c := range b { p[i+1] = c diff --git a/new_test.go b/new_test.go index 834a60a..32d7e84 100644 --- a/new_test.go +++ b/new_test.go @@ -17,45 +17,48 @@ func TestNew(t *testing.T) { _, err = New("test", "") assert.Error(t, err) + + _, err = New("test", nil) + assert.Error(t, err) + + _, err = New("test", 64) + assert.Error(t, err) + + func(p ...string) { + n := make([]interface{}, len(p)) + for i, v := range p { + n[i] = v + } + path, err = New(n...) + assert.NoError(t, err) + assert.Equal(t, path.String(), "a/b") + }("a", "b") + } func TestNewTempFile(t *testing.T) { - temp1, err := NewTempFile(TempOpt{}) + temp1, err := NewTempFile() defer func() { assert.NoError(t, temp1.Remove()) }() assert.NotNil(t, temp1) - assert.Nil(t, err) + assert.NoError(t, err) - temp2, err := NewTempFile(TempOpt{Dir: "."}) + temp2, err := NewTempFile(TempDir(".")) defer func() { assert.NoError(t, temp2.Remove()) }() assert.NotNil(t, temp2) - assert.Nil(t, err) -} - -func TestTempFile(t *testing.T) { - temp, err := NewTempFile(TempOpt{Prefix: "bla"}) - defer func() { - assert.NoError(t, temp.Remove()) - }() - - assert.NotNil(t, temp) - assert.Nil(t, err) - assert.Exactly(t, true, temp.Exists(), "new temp file exists") -} + assert.NoError(t, err) -func TestTempFileWithPattern(t *testing.T) { - temp, err := NewTempFile(TempOpt{Prefix: "bla*.dat"}) + temp, err := NewTempFile(TempPrefix("bla")) defer func() { assert.NoError(t, temp.Remove()) }() assert.NotNil(t, temp) - assert.Nil(t, err) + assert.NoError(t, err) assert.Exactly(t, true, temp.Exists(), "new temp file exists") - assert.Regexp(t, "bla.+\\.dat$", temp.String()) } func TestCwd(t *testing.T) { @@ -67,7 +70,7 @@ func TestCwd(t *testing.T) { assert.NotNil(t, cwdSub) assert.NoError(t, err) - expectedPath, _ := New(cwd.String(), ".git/config") + expectedPath, _ := New(cwd, ".git/config") assert.Equal(t, cwdSub, expectedPath) } @@ -80,6 +83,49 @@ func TestHome(t *testing.T) { assert.NotNil(t, homeSub) assert.NoError(t, err) - expectedPath, _ := New(home.String(), ".config/nvim/init.vim") + expectedPath, _ := New(home, ".config/nvim/init.vim") assert.Equal(t, homeSub, expectedPath) } + +func TestTempFileWithPattern(t *testing.T) { + temp, err := NewTempFile(TempPrefix("bla*.dat")) + defer func() { + assert.NoError(t, temp.Remove()) + }() + + assert.NotNil(t, temp) + assert.Nil(t, err) + assert.Exactly(t, true, temp.Exists(), "new temp file exists") + assert.Regexp(t, "bla.+\\.dat$", temp.String()) +} + +func TestNewTempDir(t *testing.T) { + t.Run("new temp dir", func(t *testing.T) { + temp, err := NewTempDir() + defer func() { + assert.NoError(t, temp.RemoveTree()) + }() + assert.NotNil(t, temp) + assert.NoError(t, err) + }) + + t.Run("new temp dir in current directory", func(t *testing.T) { + temp2, err := NewTempDir(TempDir(".")) + defer func() { + assert.NoError(t, temp2.RemoveTree()) + }() + assert.NotNil(t, temp2) + assert.NoError(t, err) + }) + + t.Run("new temp dir with prefix", func(t *testing.T) { + temp, err := NewTempDir(TempPrefix("bla")) + defer func() { + assert.NoError(t, temp.RemoveTree()) + }() + + assert.NotNil(t, temp) + assert.NoError(t, err) + assert.Exactly(t, true, temp.Exists(), "new temp dir exists") + }) +} diff --git a/open_test.go b/open_test.go index 80c81e8..453b14c 100644 --- a/open_test.go +++ b/open_test.go @@ -56,7 +56,7 @@ func TestLines(t *testing.T) { } func TestSpew(t *testing.T) { - temp, err := NewTempFile(TempOpt{}) + temp, err := NewTempFile() assert.NoError(t, err) err = temp.Spew("kukuc") diff --git a/parent.go b/parent.go index bf811e2..d1a046b 100644 --- a/parent.go +++ b/parent.go @@ -7,6 +7,6 @@ package pathutil // original directory or file func (path PathImpl) Parent() Path { // path.String() can't be empty - parent, _ := New(path.String(), "..") + parent, _ := New(path, "..") return parent } diff --git a/parent_test.go b/parent_test.go index 8cf9675..be0b7d4 100644 --- a/parent_test.go +++ b/parent_test.go @@ -8,7 +8,7 @@ import ( ) func TestParent(t *testing.T) { - temp, err := NewTempDir(TempOpt{}) + temp, err := NewTempDir() assert.NoError(t, err) tempDir, err := New(os.TempDir()) diff --git a/remove_test.go b/remove_test.go index 53e55dc..4d0583b 100644 --- a/remove_test.go +++ b/remove_test.go @@ -7,7 +7,7 @@ import ( ) func TestRemove(t *testing.T) { - temp, err := NewTempFile(TempOpt{}) + temp, err := NewTempFile() assert.NoError(t, err) assert.True(t, temp.Exists()) assert.NoError(t, temp.Remove()) diff --git a/rename_test.go b/rename_test.go index 09619d6..99f7ff0 100644 --- a/rename_test.go +++ b/rename_test.go @@ -7,10 +7,10 @@ import ( ) func TestRename(t *testing.T) { - a, err := NewTempFile(TempOpt{}) + a, err := NewTempFile() assert.NoError(t, err) - b, err := NewTempFile(TempOpt{}) + b, err := NewTempFile() assert.NoError(t, err) assert.NoError(t, b.Remove())