forked from mirror/pkger
commit
f788ee9125
|
@ -1,3 +1,4 @@
|
|||
.fstest
|
||||
*.log
|
||||
.DS_Store
|
||||
doc
|
||||
|
@ -28,3 +29,5 @@ bin/*
|
|||
gin-bin
|
||||
.idea/
|
||||
pkged.go
|
||||
cover.out
|
||||
.pkgtest
|
||||
|
|
6
Makefile
6
Makefile
|
@ -16,6 +16,11 @@ test: tidy
|
|||
$(GO_BIN) test -cover -tags ${TAGS} -timeout 5s ./...
|
||||
make tidy
|
||||
|
||||
cov:
|
||||
$(GO_BIN) test -coverprofile cover.out -tags ${TAGS} ./...
|
||||
go tool cover -html cover.out
|
||||
make tidy
|
||||
|
||||
ci-test:
|
||||
$(GO_BIN) test -tags ${TAGS} -race ./...
|
||||
|
||||
|
@ -43,4 +48,3 @@ release:
|
|||
make tidy
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
variables:
|
||||
GOPROXY: "https://proxy.golang.org"
|
||||
GOBIN: "$(GOPATH)/bin" # Go binaries path
|
||||
GOPATH: "$(system.defaultWorkingDirectory)/gopath" # Go workspace path
|
||||
modulePath: "$(GOPATH)/src/github.com/$(build.repository.name)" # Path to the module"s code
|
||||
|
@ -9,12 +10,12 @@ jobs:
|
|||
vmImage: "vs2017-win2016"
|
||||
strategy:
|
||||
matrix:
|
||||
go 1.11 (on):
|
||||
go_version: "1.11.13"
|
||||
GO111MODULE: "on"
|
||||
go 1.12 (on):
|
||||
go_version: "1.12.9"
|
||||
GO111MODULE: "on"
|
||||
go 1.13 (on):
|
||||
go_version: "1.13"
|
||||
GO111MODULE: "on"
|
||||
steps:
|
||||
- template: azure-tests.yml
|
||||
|
||||
|
@ -23,12 +24,12 @@ jobs:
|
|||
vmImage: "macOS-10.13"
|
||||
strategy:
|
||||
matrix:
|
||||
go 1.11 (on):
|
||||
go_version: "1.11.13"
|
||||
GO111MODULE: "on"
|
||||
go 1.12 (on):
|
||||
go_version: "1.12.9"
|
||||
GO111MODULE: "on"
|
||||
go 1.13 (on):
|
||||
go_version: "1.13"
|
||||
GO111MODULE: "on"
|
||||
steps:
|
||||
- template: azure-tests.yml
|
||||
|
||||
|
@ -37,11 +38,11 @@ jobs:
|
|||
vmImage: "ubuntu-16.04"
|
||||
strategy:
|
||||
matrix:
|
||||
go 1.11 (on):
|
||||
go_version: "1.11.13"
|
||||
GO111MODULE: "on"
|
||||
go 1.12 (on):
|
||||
go_version: "1.12.9"
|
||||
GO111MODULE: "on"
|
||||
go 1.13 (on):
|
||||
go_version: "1.13"
|
||||
GO111MODULE: "on"
|
||||
steps:
|
||||
- template: azure-tests.yml
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/markbates/pkger"
|
||||
"github.com/markbates/pkger/pkging"
|
||||
)
|
||||
|
||||
type infoCmd struct {
|
||||
|
@ -44,12 +45,12 @@ func (f *infoCmd) Exec(args []string) error {
|
|||
return err
|
||||
}
|
||||
for _, ff := range files {
|
||||
fmt.Println(pkger.NewFileInfo(ff))
|
||||
fmt.Println(pkging.NewFileInfo(ff))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Println(pkger.NewFileInfo(fi))
|
||||
fmt.Println(pkging.NewFileInfo(fi))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/markbates/pkger"
|
||||
"github.com/markbates/pkger/parser"
|
||||
"github.com/markbates/pkger/pkging"
|
||||
)
|
||||
|
||||
const outName = "pkged.go"
|
||||
|
@ -24,12 +25,12 @@ func (e *packCmd) Name() string {
|
|||
}
|
||||
|
||||
func (e *packCmd) Exec(args []string) error {
|
||||
info, err := pkger.Stat()
|
||||
info, err := pkger.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := parser.Parse(info.Dir)
|
||||
res, err := parser.Parse(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -116,7 +117,7 @@ func (e *packCmd) Flags() *flag.FlagSet {
|
|||
return e.FlagSet
|
||||
}
|
||||
|
||||
func Package(out string, paths []pkger.Path) error {
|
||||
func Package(out string, paths []pkging.Path) error {
|
||||
os.RemoveAll(out)
|
||||
|
||||
f, err := os.Create(out)
|
||||
|
@ -124,7 +125,7 @@ func Package(out string, paths []pkger.Path) error {
|
|||
return err
|
||||
}
|
||||
|
||||
c, err := pkger.Stat()
|
||||
c, err := pkger.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -132,9 +133,10 @@ func Package(out string, paths []pkger.Path) error {
|
|||
fmt.Fprintf(f, "import \"github.com/markbates/pkger\"\n\n")
|
||||
fmt.Fprintf(f, "var _ = pkger.Unpack(`")
|
||||
|
||||
if err := pkger.Pack(f, paths); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO
|
||||
// if err := pkger.Pack(f, paths); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
fmt.Fprintf(f, "`)\n")
|
||||
|
||||
|
|
|
@ -48,10 +48,11 @@ func (s *serveCmd) Exec(args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
ex := append(defaultExcludes, s.excludes...)
|
||||
if err := pkger.Exclude(f, ex...); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO
|
||||
// ex := append(defaultExcludes, s.excludes...)
|
||||
// if err := pkger.Exclude(f, ex...); err != nil {
|
||||
// return err
|
||||
// }
|
||||
defer f.Close()
|
||||
fmt.Println(f.Path())
|
||||
|
||||
|
|
|
@ -8,7 +8,10 @@ import (
|
|||
)
|
||||
|
||||
func walk(args []string) error {
|
||||
err := pkger.Walk(".", func(path pkger.Path, info os.FileInfo) error {
|
||||
err := pkger.Walk(".", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(path)
|
||||
return nil
|
||||
})
|
||||
|
|
36
create.go
36
create.go
|
@ -1,36 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Create(name string) (*File, error) {
|
||||
pt, err := Parse(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
her, err := Info(pt.Pkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f := &File{
|
||||
path: pt,
|
||||
her: her,
|
||||
info: &FileInfo{
|
||||
name: pt.Name,
|
||||
mode: 0666,
|
||||
modTime: time.Now(),
|
||||
virtual: true,
|
||||
},
|
||||
}
|
||||
|
||||
filesCache.Store(pt, f)
|
||||
|
||||
dir := filepath.Dir(pt.Name)
|
||||
if err := MkdirAll(dir, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Testg_Create(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
f, err := Create("/hello.txt")
|
||||
r.NoError(err)
|
||||
r.NotNil(f)
|
||||
|
||||
fi, err := f.Stat()
|
||||
r.NoError(err)
|
||||
|
||||
r.Equal("/hello.txt", fi.Name())
|
||||
r.Equal(os.FileMode(0666), fi.Mode())
|
||||
r.NotZero(fi.ModTime())
|
||||
|
||||
her := f.her
|
||||
r.NotZero(her)
|
||||
r.Equal("github.com/markbates/pkger", her.ImportPath)
|
||||
}
|
||||
|
||||
func Testg_Create_Write(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
f, err := Create("/hello.txt")
|
||||
r.NoError(err)
|
||||
r.NotNil(f)
|
||||
|
||||
fi, err := f.Stat()
|
||||
r.NoError(err)
|
||||
r.Zero(fi.Size())
|
||||
|
||||
r.Equal("/hello.txt", fi.Name())
|
||||
|
||||
mt := fi.ModTime()
|
||||
r.NotZero(mt)
|
||||
|
||||
sz, err := io.Copy(f, strings.NewReader(radio))
|
||||
r.NoError(err)
|
||||
r.Equal(int64(1381), sz)
|
||||
|
||||
r.NoError(f.Close())
|
||||
r.Equal(int64(1381), fi.Size())
|
||||
r.NotZero(fi.ModTime())
|
||||
r.NotEqual(mt, fi.ModTime())
|
||||
}
|
55
dir.go
55
dir.go
|
@ -1,55 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
func MkdirAll(p string, perm os.FileMode) error {
|
||||
path, err := Parse(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
root := path.Name
|
||||
|
||||
cur, err := Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for root != "" && root != "/" && root != "\\" {
|
||||
pt := Path{
|
||||
Pkg: path.Pkg,
|
||||
Name: root,
|
||||
}
|
||||
if _, ok := filesCache.Load(pt); ok {
|
||||
root = filepath.Dir(root)
|
||||
continue
|
||||
}
|
||||
f := &File{
|
||||
path: pt,
|
||||
her: cur,
|
||||
info: &FileInfo{
|
||||
name: pt.Name,
|
||||
mode: 0666,
|
||||
modTime: time.Now(),
|
||||
virtual: true,
|
||||
},
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.info.isDir = true
|
||||
f.info.mode = perm
|
||||
f.info.virtual = true
|
||||
if err := f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
filesCache.Store(pt, f)
|
||||
root = filepath.Dir(root)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
module github.com/markbates/pkger/examples/app
|
||||
|
||||
go 1.12
|
||||
go 1.13
|
||||
|
||||
require github.com/markbates/pkger v0.0.0-20190830024022-c5e3a7de4d41
|
||||
require github.com/markbates/pkger v0.0.0
|
||||
|
||||
replace github.com/markbates/pkger => ../../
|
||||
|
|
|
@ -8,8 +8,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc=
|
||||
github.com/markbates/pkger v0.0.0-20190830024022-c5e3a7de4d41 h1:bFEHpLBby7Qdvq92qw0TudeOfUKIZLcrweE/MMlNueI=
|
||||
github.com/markbates/pkger v0.0.0-20190830024022-c5e3a7de4d41/go.mod h1:M9VeozwduQUCr6z54kJrK9JegpbOv4wiePSbgSbFOew=
|
||||
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
|
|
@ -18,16 +18,19 @@ func main() {
|
|||
}
|
||||
defer pub.Close()
|
||||
|
||||
fi, err := pub.Stat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(fi)
|
||||
fmt.Println(pub.Path())
|
||||
|
||||
mux.Handle("/t", http.StripPrefix("/t", tmplHandler()))
|
||||
mux.Handle("/", http.FileServer(pub))
|
||||
|
||||
log.Fatal(http.ListenAndServe(":3000", mux))
|
||||
log.Fatal(http.ListenAndServe(":3000", logger(mux)))
|
||||
}
|
||||
|
||||
func logger(h http.Handler) http.HandlerFunc {
|
||||
return func(res http.ResponseWriter, req *http.Request) {
|
||||
log.Println(req.Method, req.URL.String())
|
||||
h.ServeHTTP(res, req)
|
||||
}
|
||||
}
|
||||
|
||||
func tmplHandler() http.HandlerFunc {
|
||||
|
|
|
@ -2,4 +2,7 @@ module github.com/markbates/pkger/examples/extfile
|
|||
|
||||
go 1.12
|
||||
|
||||
require github.com/markbates/pkger v0.0.0-20190830024022-c5e3a7de4d41
|
||||
require github.com/markbates/pkger v0.0.0
|
||||
|
||||
replace github.com/markbates/pkger => ../../
|
||||
|
||||
|
|
|
@ -2,4 +2,7 @@ module github.com/markbates/pkger/examples/httpserver
|
|||
|
||||
go 1.12
|
||||
|
||||
require github.com/markbates/pkger v0.0.0-20190830024022-c5e3a7de4d41
|
||||
require github.com/markbates/pkger v0.0.0
|
||||
|
||||
replace github.com/markbates/pkger => ../../
|
||||
|
||||
|
|
|
@ -2,4 +2,7 @@ module github.com/markbates/pkger/examples/walk
|
|||
|
||||
go 1.12
|
||||
|
||||
require github.com/markbates/pkger v0.0.0-20190830024022-c5e3a7de4d41
|
||||
require github.com/markbates/pkger v0.0.0
|
||||
|
||||
replace github.com/markbates/pkger => ../../
|
||||
|
||||
|
|
179
file.go
179
file.go
|
@ -1,179 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
)
|
||||
|
||||
const timeFmt = time.RFC3339Nano
|
||||
|
||||
var _ http.File = &File{}
|
||||
|
||||
type File struct {
|
||||
info *FileInfo
|
||||
her here.Info
|
||||
path Path
|
||||
data []byte
|
||||
parent Path
|
||||
writer *bytes.Buffer
|
||||
reader io.Reader
|
||||
excludes []string
|
||||
}
|
||||
|
||||
func (f *File) Seek(offset int64, whence int) (int64, error) {
|
||||
if sk, ok := f.reader.(io.Seeker); ok {
|
||||
return sk.Seek(offset, whence)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (f *File) Close() error {
|
||||
defer func() {
|
||||
f.reader = nil
|
||||
f.writer = nil
|
||||
}()
|
||||
if f.reader != nil {
|
||||
if c, ok := f.reader.(io.Closer); ok {
|
||||
if err := c.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if f.writer == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
f.data = f.writer.Bytes()
|
||||
|
||||
fi := f.info
|
||||
fi.size = int64(len(f.data))
|
||||
fi.modTime = time.Now()
|
||||
f.info = fi
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) Read(p []byte) (int, error) {
|
||||
if len(f.data) > 0 && f.reader == nil {
|
||||
f.reader = bytes.NewReader(f.data)
|
||||
}
|
||||
|
||||
if f.reader != nil {
|
||||
return f.reader.Read(p)
|
||||
}
|
||||
|
||||
of, err := f.her.Open(f.FilePath())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
f.reader = of
|
||||
return f.reader.Read(p)
|
||||
}
|
||||
|
||||
func (f *File) Write(b []byte) (int, error) {
|
||||
if f.writer == nil {
|
||||
f.writer = &bytes.Buffer{}
|
||||
}
|
||||
i, err := f.writer.Write(b)
|
||||
return i, err
|
||||
}
|
||||
|
||||
func (f File) Info() here.Info {
|
||||
return f.her
|
||||
}
|
||||
|
||||
func (f File) Stat() (os.FileInfo, error) {
|
||||
if f.info == nil {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
return f.info, nil
|
||||
}
|
||||
|
||||
func (f File) Name() string {
|
||||
return f.info.Name()
|
||||
}
|
||||
|
||||
func (f File) FilePath() string {
|
||||
return f.her.FilePath(f.Name())
|
||||
}
|
||||
|
||||
func (f File) Path() Path {
|
||||
return f.path
|
||||
}
|
||||
|
||||
func (f File) String() string {
|
||||
return f.Path().String()
|
||||
}
|
||||
|
||||
func (f File) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if st.Flag('+') {
|
||||
b, err := json.MarshalIndent(f, "", " ")
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(st, string(b))
|
||||
return
|
||||
}
|
||||
fmt.Fprint(st, f.String())
|
||||
case 'q':
|
||||
fmt.Fprintf(st, "%q", f.String())
|
||||
default:
|
||||
fmt.Fprint(st, f.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
|
||||
var infos []os.FileInfo
|
||||
err := Walk(f.Name(), func(pt Path, info os.FileInfo) error {
|
||||
if count > 0 && len(infos) == count {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
for _, x := range f.excludes {
|
||||
rx, err := regexp.Compile(x)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rx.MatchString(pt.Name) {
|
||||
if info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if pt.Name == f.parent.Name {
|
||||
return nil
|
||||
}
|
||||
if f.parent.Name != "/" {
|
||||
info = WithName(strings.TrimPrefix(info.Name(), f.parent.Name), info)
|
||||
}
|
||||
infos = append(infos, info)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*os.PathError); ok {
|
||||
return infos, nil
|
||||
}
|
||||
if err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return infos, nil
|
||||
|
||||
}
|
139
file_info.go
139
file_info.go
|
@ -1,139 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
isDir bool
|
||||
virtual bool
|
||||
sys interface{}
|
||||
}
|
||||
|
||||
func (f *FileInfo) String() string {
|
||||
b, _ := json.MarshalIndent(f, "", " ")
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (f *FileInfo) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]interface{}{
|
||||
"name": f.name,
|
||||
"size": f.size,
|
||||
"mode": f.mode,
|
||||
"modTime": f.modTime.Format(timeFmt),
|
||||
"isDir": f.isDir,
|
||||
"virtual": true,
|
||||
"sys": f.sys,
|
||||
})
|
||||
}
|
||||
|
||||
func (f *FileInfo) UnmarshalJSON(b []byte) error {
|
||||
m := map[string]interface{}{}
|
||||
if err := json.Unmarshal(b, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
|
||||
f.name, ok = m["name"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("could not determine name %q", m["name"])
|
||||
}
|
||||
|
||||
size, ok := m["size"].(float64)
|
||||
if !ok {
|
||||
return fmt.Errorf("could not determine size %q", m["size"])
|
||||
}
|
||||
f.size = int64(size)
|
||||
|
||||
mode, ok := m["mode"].(float64)
|
||||
if !ok {
|
||||
return fmt.Errorf("could not determine mode %q", m["mode"])
|
||||
}
|
||||
f.mode = os.FileMode(mode)
|
||||
|
||||
modTime, ok := m["modTime"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("could not determine modTime %q", m["modTime"])
|
||||
}
|
||||
t, err := time.Parse(timeFmt, modTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.modTime = t
|
||||
|
||||
f.isDir, ok = m["isDir"].(bool)
|
||||
if !ok {
|
||||
return fmt.Errorf("could not determine isDir %q", m["isDir"])
|
||||
}
|
||||
f.sys = m["sys"]
|
||||
f.virtual = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileInfo) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
func (f *FileInfo) Size() int64 {
|
||||
return f.size
|
||||
}
|
||||
|
||||
func (f *FileInfo) Mode() os.FileMode {
|
||||
return f.mode
|
||||
}
|
||||
|
||||
func (f *FileInfo) ModTime() time.Time {
|
||||
return f.modTime
|
||||
}
|
||||
|
||||
func (f *FileInfo) IsDir() bool {
|
||||
return f.isDir
|
||||
}
|
||||
|
||||
func (f *FileInfo) Sys() interface{} {
|
||||
return f.sys
|
||||
}
|
||||
|
||||
var _ os.FileInfo = &FileInfo{}
|
||||
|
||||
func NewFileInfo(info os.FileInfo) *FileInfo {
|
||||
fi := &FileInfo{
|
||||
name: cleanName(info.Name()),
|
||||
size: info.Size(),
|
||||
mode: info.Mode(),
|
||||
modTime: info.ModTime(),
|
||||
isDir: info.IsDir(),
|
||||
sys: info.Sys(),
|
||||
}
|
||||
return fi
|
||||
}
|
||||
|
||||
func WithName(name string, info os.FileInfo) *FileInfo {
|
||||
if ft, ok := info.(*FileInfo); ok {
|
||||
ft.name = cleanName(name)
|
||||
return ft
|
||||
}
|
||||
|
||||
fo := NewFileInfo(info)
|
||||
fo.name = cleanName(name)
|
||||
return fo
|
||||
}
|
||||
|
||||
func cleanName(s string) string {
|
||||
if strings.Contains(s, "\\") {
|
||||
s = strings.Replace(s, "\\", "/", -1)
|
||||
}
|
||||
if !strings.HasPrefix(s, "/") {
|
||||
s = "/" + s
|
||||
}
|
||||
return s
|
||||
}
|
108
file_test.go
108
file_test.go
|
@ -1,108 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_File_Open(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
f, err := Open("/file_test.go")
|
||||
r.NoError(err)
|
||||
|
||||
r.Equal("/file_test.go", f.Name())
|
||||
|
||||
b, err := ioutil.ReadAll(f)
|
||||
r.NoError(err)
|
||||
r.Contains(string(b), "Test_File_Open")
|
||||
r.NoError(f.Close())
|
||||
}
|
||||
|
||||
func Test_File_Open_Dir(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
f, err := Open("/cmd")
|
||||
r.NoError(err)
|
||||
|
||||
r.Equal("/cmd", f.Name())
|
||||
|
||||
r.NoError(f.Close())
|
||||
}
|
||||
|
||||
func Test_File_Read_Memory(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
f, err := Open("/file_test.go")
|
||||
r.NoError(err)
|
||||
f.data = []byte("hi!")
|
||||
|
||||
r.Equal("/file_test.go", f.Name())
|
||||
|
||||
b, err := ioutil.ReadAll(f)
|
||||
r.NoError(err)
|
||||
r.Equal(string(b), "hi!")
|
||||
r.NoError(f.Close())
|
||||
}
|
||||
|
||||
func Test_File_Write(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
f, err := Create("/hello.txt")
|
||||
r.NoError(err)
|
||||
r.NotNil(f)
|
||||
|
||||
fi, err := f.Stat()
|
||||
r.NoError(err)
|
||||
r.Zero(fi.Size())
|
||||
|
||||
r.Equal("/hello.txt", fi.Name())
|
||||
|
||||
mt := fi.ModTime()
|
||||
r.NotZero(mt)
|
||||
|
||||
sz, err := io.Copy(f, strings.NewReader(radio))
|
||||
r.NoError(err)
|
||||
r.Equal(int64(1381), sz)
|
||||
|
||||
// because windows can't handle the time precisely
|
||||
// enough, we have to *force* just a smidge of time
|
||||
// to ensure the two ModTime's are different.
|
||||
// i know, i hate it too.
|
||||
time.Sleep(time.Millisecond)
|
||||
r.NoError(f.Close())
|
||||
r.Equal(int64(1381), fi.Size())
|
||||
r.NotZero(fi.ModTime())
|
||||
r.NotEqual(mt, fi.ModTime())
|
||||
}
|
||||
|
||||
func Test_File_JSON(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
f, err := createFile("/radio.radio")
|
||||
r.NoError(err)
|
||||
r.NotNil(f)
|
||||
|
||||
bi, err := f.Stat()
|
||||
r.NoError(err)
|
||||
|
||||
mj, err := json.Marshal(f)
|
||||
r.NoError(err)
|
||||
|
||||
f2 := &File{}
|
||||
|
||||
r.NoError(json.Unmarshal(mj, f2))
|
||||
|
||||
ai, err := f2.Stat()
|
||||
r.NoError(err)
|
||||
|
||||
r.Equal(bi.Size(), ai.Size())
|
||||
|
||||
r.Equal(string(f.data), string(f2.data))
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package pkger
|
||||
|
||||
func Exclude(fl *File, excludes ...string) error {
|
||||
fl.excludes = append(fl.excludes, excludes...)
|
||||
return nil
|
||||
}
|
2
go.mod
2
go.mod
|
@ -1,6 +1,6 @@
|
|||
module github.com/markbates/pkger
|
||||
|
||||
go 1.12
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
// Dir attempts to gather info for the requested directory.
|
||||
func Dir(p string) (Info, error) {
|
||||
return Cache(p, func(p string) (Info, error) {
|
||||
var i Info
|
||||
|
||||
fi, err := os.Stat(p)
|
||||
|
@ -38,4 +39,5 @@ func Dir(p string) (Info, error) {
|
|||
}
|
||||
|
||||
return i, nil
|
||||
})
|
||||
}
|
||||
|
|
18
here/here.go
18
here/here.go
|
@ -4,8 +4,13 @@ import (
|
|||
"bytes"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var cache = &infoMap{
|
||||
data: &sync.Map{},
|
||||
}
|
||||
|
||||
func run(n string, args ...string) ([]byte, error) {
|
||||
c := exec.Command(n, args...)
|
||||
|
||||
|
@ -19,3 +24,16 @@ func run(n string, args ...string) ([]byte, error) {
|
|||
|
||||
return bb.Bytes(), nil
|
||||
}
|
||||
|
||||
func Cache(p string, fn func(string) (Info, error)) (Info, error) {
|
||||
i, ok := cache.Load(p)
|
||||
if ok {
|
||||
return i, nil
|
||||
}
|
||||
i, err := fn(p)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
cache.Store(p, i)
|
||||
return i, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
// Code generated by github.com/gobuffalo/mapgen. DO NOT EDIT.
|
||||
|
||||
package here
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// infoMap wraps sync.Map and uses the following types:
|
||||
// key: string
|
||||
// value: Info
|
||||
type infoMap struct {
|
||||
data *sync.Map
|
||||
}
|
||||
|
||||
// Delete the key from the map
|
||||
func (m *infoMap) Delete(key string) {
|
||||
m.data.Delete(key)
|
||||
}
|
||||
|
||||
// Load the key from the map.
|
||||
// Returns Info or bool.
|
||||
// A false return indicates either the key was not found
|
||||
// or the value is not of type Info
|
||||
func (m *infoMap) Load(key string) (Info, bool) {
|
||||
i, ok := m.data.Load(key)
|
||||
if !ok {
|
||||
return Info{}, false
|
||||
}
|
||||
s, ok := i.(Info)
|
||||
return s, ok
|
||||
}
|
||||
|
||||
// LoadOrStore will return an existing key or
|
||||
// store the value if not already in the map
|
||||
func (m *infoMap) LoadOrStore(key string, value Info) (Info, bool) {
|
||||
i, _ := m.data.LoadOrStore(key, value)
|
||||
s, ok := i.(Info)
|
||||
return s, ok
|
||||
}
|
||||
|
||||
// LoadOr will return an existing key or
|
||||
// run the function and store the results
|
||||
func (m *infoMap) LoadOr(key string, fn func(*infoMap) (Info, bool)) (Info, bool) {
|
||||
i, ok := m.Load(key)
|
||||
if ok {
|
||||
return i, ok
|
||||
}
|
||||
i, ok = fn(m)
|
||||
if ok {
|
||||
m.Store(key, i)
|
||||
return i, ok
|
||||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
// Range over the Info values in the map
|
||||
func (m *infoMap) Range(f func(key string, value Info) bool) {
|
||||
m.data.Range(func(k, v interface{}) bool {
|
||||
key, ok := k.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
value, ok := v.(Info)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return f(key, value)
|
||||
})
|
||||
}
|
||||
|
||||
// Store a Info in the map
|
||||
func (m *infoMap) Store(key string, value Info) {
|
||||
m.data.Store(key, value)
|
||||
}
|
||||
|
||||
// Keys returns a list of keys in the map
|
||||
func (m *infoMap) Keys() []string {
|
||||
var keys []string
|
||||
m.Range(func(key string, value Info) bool {
|
||||
keys = append(keys, key)
|
||||
return true
|
||||
})
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package here
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Package attempts to gather info for the requested package.
|
||||
//
|
||||
|
@ -12,6 +14,7 @@ import "encoding/json"
|
|||
// returned `Info` value and pass it to the `Dir(string) (Info, error)`
|
||||
// function to return the complete data.
|
||||
func Package(p string) (Info, error) {
|
||||
return Cache(p, func(p string) (Info, error) {
|
||||
var i Info
|
||||
b, err := run("go", "list", "-json", "-find", p)
|
||||
if err != nil {
|
||||
|
@ -22,4 +25,5 @@ func Package(p string) (Info, error) {
|
|||
}
|
||||
|
||||
return i, nil
|
||||
})
|
||||
}
|
||||
|
|
17
info.go
17
info.go
|
@ -1,17 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import "github.com/markbates/pkger/here"
|
||||
|
||||
func Info(p string) (here.Info, error) {
|
||||
info, ok := infosCache.Load(p)
|
||||
if ok {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
info, err := here.Package(p)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
infosCache.Store(p, info)
|
||||
return info, nil
|
||||
}
|
|
@ -1,23 +1,25 @@
|
|||
// Code generated by github.com/gobuffalo/mapgen. DO NOT EDIT.
|
||||
|
||||
package pkger
|
||||
package maps
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/markbates/pkger/pkging"
|
||||
)
|
||||
|
||||
// filesMap wraps sync.Map and uses the following types:
|
||||
// key: Path
|
||||
// value: *File
|
||||
type filesMap struct {
|
||||
// Files wraps sync.Map and uses the following types:
|
||||
// key: pkging.Path
|
||||
// value: pkging.File
|
||||
type Files struct {
|
||||
data *sync.Map
|
||||
once *sync.Once
|
||||
}
|
||||
|
||||
func (m *filesMap) Data() *sync.Map {
|
||||
func (m *Files) Data() *sync.Map {
|
||||
if m.once == nil {
|
||||
m.once = &sync.Once{}
|
||||
}
|
||||
|
@ -29,7 +31,7 @@ func (m *filesMap) Data() *sync.Map {
|
|||
return m.data
|
||||
}
|
||||
|
||||
func (m *filesMap) MarshalJSON() ([]byte, error) {
|
||||
func (m *Files) MarshalJSON() ([]byte, error) {
|
||||
var err error
|
||||
mm := map[string]interface{}{}
|
||||
m.Data().Range(func(key, value interface{}) bool {
|
||||
|
@ -49,14 +51,14 @@ func (m *filesMap) MarshalJSON() ([]byte, error) {
|
|||
return json.Marshal(mm)
|
||||
}
|
||||
|
||||
func (m *filesMap) UnmarshalJSON(b []byte) error {
|
||||
mm := map[string]*File{}
|
||||
func (m *Files) UnmarshalJSON(b []byte) error {
|
||||
mm := map[string]pkging.File{}
|
||||
|
||||
if err := json.Unmarshal(b, &mm); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range mm {
|
||||
var pt Path
|
||||
var pt pkging.Path
|
||||
if err := json.Unmarshal([]byte(k), &pt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -66,54 +68,31 @@ func (m *filesMap) UnmarshalJSON(b []byte) error {
|
|||
}
|
||||
|
||||
// Delete the key from the map
|
||||
func (m *filesMap) Delete(key Path) {
|
||||
func (m *Files) Delete(key pkging.Path) {
|
||||
m.Data().Delete(key)
|
||||
}
|
||||
|
||||
// Load the key from the map.
|
||||
// Returns *File or bool.
|
||||
// Returns pkging.File or bool.
|
||||
// A false return indicates either the key was not found
|
||||
// or the value is not of type *File
|
||||
func (m *filesMap) Load(key Path) (*File, bool) {
|
||||
// or the value is not of type pkging.File
|
||||
func (m *Files) Load(key pkging.Path) (pkging.File, bool) {
|
||||
i, ok := m.Data().Load(key)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
s, ok := i.(*File)
|
||||
s, ok := i.(pkging.File)
|
||||
return s, ok
|
||||
}
|
||||
|
||||
// LoadOrStore will return an existing key or
|
||||
// store the value if not already in the map
|
||||
func (m *filesMap) LoadOrStore(key Path, value *File) (*File, bool) {
|
||||
i, _ := m.Data().LoadOrStore(key, value)
|
||||
s, ok := i.(*File)
|
||||
return s, ok
|
||||
}
|
||||
|
||||
// LoadOr will return an existing key or
|
||||
// run the function and store the results
|
||||
func (m *filesMap) LoadOr(key Path, fn func(*filesMap) (*File, bool)) (*File, bool) {
|
||||
i, ok := m.Load(key)
|
||||
if ok {
|
||||
return i, ok
|
||||
}
|
||||
i, ok = fn(m)
|
||||
if ok {
|
||||
m.Store(key, i)
|
||||
return i, ok
|
||||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
// Range over the *File values in the map
|
||||
func (m *filesMap) Range(f func(key Path, value *File) bool) {
|
||||
// Range over the pkging.File values in the map
|
||||
func (m *Files) Range(f func(key pkging.Path, value pkging.File) bool) {
|
||||
m.Data().Range(func(k, v interface{}) bool {
|
||||
key, ok := k.(Path)
|
||||
key, ok := k.(pkging.Path)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
value, ok := v.(*File)
|
||||
value, ok := v.(pkging.File)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
@ -121,15 +100,15 @@ func (m *filesMap) Range(f func(key Path, value *File) bool) {
|
|||
})
|
||||
}
|
||||
|
||||
// Store a *File in the map
|
||||
func (m *filesMap) Store(key Path, value *File) {
|
||||
// Store a pkging.File in the map
|
||||
func (m *Files) Store(key pkging.Path, value pkging.File) {
|
||||
m.Data().Store(key, value)
|
||||
}
|
||||
|
||||
// Keys returns a list of keys in the map
|
||||
func (m *filesMap) Keys() []Path {
|
||||
var keys []Path
|
||||
m.Range(func(key Path, value *File) bool {
|
||||
func (m *Files) Keys() []pkging.Path {
|
||||
var keys []pkging.Path
|
||||
m.Range(func(key pkging.Path, value pkging.File) bool {
|
||||
keys = append(keys, key)
|
||||
return true
|
||||
})
|
||||
|
@ -139,6 +118,6 @@ func (m *filesMap) Keys() []Path {
|
|||
return keys
|
||||
}
|
||||
|
||||
func (m *filesMap) String() string {
|
||||
func (m *Files) String() string {
|
||||
return fmt.Sprintf("%v", m.Keys())
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by github.com/markbates/pkger/mapgen. DO NOT EDIT.
|
||||
|
||||
package pkger
|
||||
package maps
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -11,15 +11,15 @@ import (
|
|||
"github.com/markbates/pkger/here"
|
||||
)
|
||||
|
||||
// infosMap wraps sync.Map and uses the following types:
|
||||
// Infos wraps sync.Map and uses the following types:
|
||||
// key: string
|
||||
// value: here.Info
|
||||
type infosMap struct {
|
||||
type Infos struct {
|
||||
data *sync.Map
|
||||
once *sync.Once
|
||||
}
|
||||
|
||||
func (m *infosMap) Data() *sync.Map {
|
||||
func (m *Infos) Data() *sync.Map {
|
||||
if m.once == nil {
|
||||
m.once = &sync.Once{}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ func (m *infosMap) Data() *sync.Map {
|
|||
return m.data
|
||||
}
|
||||
|
||||
func (m *infosMap) MarshalJSON() ([]byte, error) {
|
||||
func (m *Infos) MarshalJSON() ([]byte, error) {
|
||||
mm := map[string]interface{}{}
|
||||
m.data.Range(func(key, value interface{}) bool {
|
||||
mm[fmt.Sprintf("%s", key)] = value
|
||||
|
@ -40,7 +40,7 @@ func (m *infosMap) MarshalJSON() ([]byte, error) {
|
|||
return json.Marshal(mm)
|
||||
}
|
||||
|
||||
func (m *infosMap) UnmarshalJSON(b []byte) error {
|
||||
func (m *Infos) UnmarshalJSON(b []byte) error {
|
||||
mm := map[string]here.Info{}
|
||||
|
||||
if err := json.Unmarshal(b, &mm); err != nil {
|
||||
|
@ -53,7 +53,7 @@ func (m *infosMap) UnmarshalJSON(b []byte) error {
|
|||
}
|
||||
|
||||
// Delete the key from the map
|
||||
func (m *infosMap) Delete(key string) {
|
||||
func (m *Infos) Delete(key string) {
|
||||
m.Data().Delete(key)
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ func (m *infosMap) Delete(key string) {
|
|||
// Returns here.Info or bool.
|
||||
// A false return indicates either the key was not found
|
||||
// or the value is not of type here.Info
|
||||
func (m *infosMap) Load(key string) (here.Info, bool) {
|
||||
func (m *Infos) Load(key string) (here.Info, bool) {
|
||||
m.Data()
|
||||
i, ok := m.data.Load(key)
|
||||
if !ok {
|
||||
|
@ -71,31 +71,8 @@ func (m *infosMap) Load(key string) (here.Info, bool) {
|
|||
return s, ok
|
||||
}
|
||||
|
||||
// LoadOrStore will return an existing key or
|
||||
// store the value if not already in the map
|
||||
func (m *infosMap) LoadOrStore(key string, value here.Info) (here.Info, bool) {
|
||||
i, _ := m.Data().LoadOrStore(key, value)
|
||||
s, ok := i.(here.Info)
|
||||
return s, ok
|
||||
}
|
||||
|
||||
// LoadOr will return an existing key or
|
||||
// run the function and store the results
|
||||
func (m *infosMap) LoadOr(key string, fn func(*infosMap) (here.Info, bool)) (here.Info, bool) {
|
||||
i, ok := m.Load(key)
|
||||
if ok {
|
||||
return i, ok
|
||||
}
|
||||
i, ok = fn(m)
|
||||
if ok {
|
||||
m.Store(key, i)
|
||||
return i, ok
|
||||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
// Range over the here.Info values in the map
|
||||
func (m *infosMap) Range(f func(key string, value here.Info) bool) {
|
||||
func (m *Infos) Range(f func(key string, value here.Info) bool) {
|
||||
m.Data().Range(func(k, v interface{}) bool {
|
||||
key, ok := k.(string)
|
||||
if !ok {
|
||||
|
@ -110,12 +87,12 @@ func (m *infosMap) Range(f func(key string, value here.Info) bool) {
|
|||
}
|
||||
|
||||
// Store a here.Info in the map
|
||||
func (m *infosMap) Store(key string, value here.Info) {
|
||||
func (m *Infos) Store(key string, value here.Info) {
|
||||
m.Data().Store(key, value)
|
||||
}
|
||||
|
||||
// Keys returns a list of keys in the map
|
||||
func (m *infosMap) Keys() []string {
|
||||
func (m *Infos) Keys() []string {
|
||||
var keys []string
|
||||
m.Range(func(key string, value here.Info) bool {
|
||||
keys = append(keys, key)
|
|
@ -0,0 +1,165 @@
|
|||
package maps
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
"github.com/markbates/pkger/pkging"
|
||||
)
|
||||
|
||||
// Paths wraps sync.Map and uses the following types:
|
||||
// key: string
|
||||
// value: Path
|
||||
type Paths struct {
|
||||
Current here.Info
|
||||
data *sync.Map
|
||||
once *sync.Once
|
||||
}
|
||||
|
||||
func (m *Paths) Data() *sync.Map {
|
||||
if m.once == nil {
|
||||
m.once = &sync.Once{}
|
||||
}
|
||||
m.once.Do(func() {
|
||||
if m.data == nil {
|
||||
m.data = &sync.Map{}
|
||||
}
|
||||
})
|
||||
return m.data
|
||||
}
|
||||
|
||||
func (m *Paths) MarshalJSON() ([]byte, error) {
|
||||
mm := map[string]interface{}{}
|
||||
m.Data().Range(func(key, value interface{}) bool {
|
||||
mm[fmt.Sprintf("%s", key)] = value
|
||||
return true
|
||||
})
|
||||
return json.Marshal(mm)
|
||||
}
|
||||
|
||||
func (m *Paths) UnmarshalJSON(b []byte) error {
|
||||
mm := map[string]pkging.Path{}
|
||||
|
||||
if err := json.Unmarshal(b, &mm); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range mm {
|
||||
m.Store(k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete the key from the map
|
||||
func (m *Paths) Delete(key string) {
|
||||
m.Data().Delete(key)
|
||||
}
|
||||
|
||||
// Load the key from the map.
|
||||
// Returns Path or bool.
|
||||
// A false return indicates either the key was not found
|
||||
// or the value is not of type Path
|
||||
func (m *Paths) Load(key string) (pkging.Path, bool) {
|
||||
i, ok := m.Data().Load(key)
|
||||
if !ok {
|
||||
return pkging.Path{}, false
|
||||
}
|
||||
s, ok := i.(pkging.Path)
|
||||
return s, ok
|
||||
}
|
||||
|
||||
// Range over the Path values in the map
|
||||
func (m *Paths) Range(f func(key string, value pkging.Path) bool) {
|
||||
m.Data().Range(func(k, v interface{}) bool {
|
||||
key, ok := k.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
value, ok := v.(pkging.Path)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return f(key, value)
|
||||
})
|
||||
}
|
||||
|
||||
// Store a Path in the map
|
||||
func (m *Paths) Store(key string, value pkging.Path) {
|
||||
m.Data().Store(key, value)
|
||||
}
|
||||
|
||||
// Keys returns a list of keys in the map
|
||||
func (m *Paths) Keys() []string {
|
||||
var keys []string
|
||||
m.Range(func(key string, value pkging.Path) bool {
|
||||
keys = append(keys, key)
|
||||
return true
|
||||
})
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func (m *Paths) Parse(p string) (pkging.Path, error) {
|
||||
p = strings.TrimSpace(p)
|
||||
p = filepath.Clean(p)
|
||||
p = strings.TrimPrefix(p, m.Current.Dir)
|
||||
|
||||
p = strings.Replace(p, "\\", "/", -1)
|
||||
p = strings.TrimSpace(p)
|
||||
|
||||
pt, ok := m.Load(p)
|
||||
if ok {
|
||||
return pt, nil
|
||||
}
|
||||
if len(p) == 0 || p == ":" {
|
||||
return m.build("", "", "")
|
||||
}
|
||||
|
||||
res := pathrx.FindAllStringSubmatch(p, -1)
|
||||
if len(res) == 0 {
|
||||
return pt, fmt.Errorf("could not parse %q", p)
|
||||
}
|
||||
|
||||
matches := res[0]
|
||||
|
||||
if len(matches) != 4 {
|
||||
return pt, fmt.Errorf("could not parse %q", p)
|
||||
}
|
||||
|
||||
return m.build(p, matches[1], matches[3])
|
||||
}
|
||||
|
||||
var pathrx = regexp.MustCompile("([^:]+)(:(/.+))?")
|
||||
|
||||
func (m *Paths) build(p, pkg, name string) (pkging.Path, error) {
|
||||
pt := pkging.Path{
|
||||
Pkg: pkg,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
if strings.HasPrefix(pt.Pkg, "/") || len(pt.Pkg) == 0 {
|
||||
pt.Name = pt.Pkg
|
||||
pt.Pkg = m.Current.ImportPath
|
||||
}
|
||||
|
||||
if len(pt.Name) == 0 {
|
||||
pt.Name = "/"
|
||||
}
|
||||
|
||||
if pt.Pkg == pt.Name {
|
||||
pt.Pkg = m.Current.ImportPath
|
||||
pt.Name = "/"
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(pt.Name, "/") {
|
||||
pt.Name = "/" + pt.Name
|
||||
}
|
||||
pt.Name = strings.TrimPrefix(pt.Name, m.Current.Dir)
|
||||
m.Store(p, pt)
|
||||
return pt, nil
|
||||
}
|
83
json.go
83
json.go
|
@ -1,83 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
)
|
||||
|
||||
type jason struct {
|
||||
Files *filesMap `json:"files"`
|
||||
Infos *infosMap `json:"infos"`
|
||||
Paths *pathsMap `json:"paths"`
|
||||
CurrentInfo here.Info `json:"current_info"`
|
||||
}
|
||||
|
||||
func (f File) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{}
|
||||
m["info"] = f.info
|
||||
m["her"] = f.her
|
||||
m["path"] = f.path
|
||||
m["data"] = f.data
|
||||
m["parent"] = f.parent
|
||||
if !f.info.virtual {
|
||||
if len(f.data) == 0 && !f.info.IsDir() {
|
||||
b, err := ioutil.ReadAll(&f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m["data"] = b
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func (f *File) UnmarshalJSON(b []byte) error {
|
||||
m := map[string]json.RawMessage{}
|
||||
if err := json.Unmarshal(b, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, ok := m["info"]
|
||||
if !ok {
|
||||
return fmt.Errorf("missing info")
|
||||
}
|
||||
|
||||
f.info = &FileInfo{}
|
||||
if err := json.Unmarshal(info, f.info); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
her, ok := m["her"]
|
||||
if !ok {
|
||||
return fmt.Errorf("missing her")
|
||||
}
|
||||
if err := json.Unmarshal(her, &f.her); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path, ok := m["path"]
|
||||
if !ok {
|
||||
return fmt.Errorf("missing path")
|
||||
}
|
||||
if err := json.Unmarshal(path, &f.path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parent, ok := m["parent"]
|
||||
if !ok {
|
||||
return fmt.Errorf("missing parent")
|
||||
}
|
||||
if err := json.Unmarshal(parent, &f.parent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(m["data"], &f.data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
79
open.go
79
open.go
|
@ -1,79 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (f *File) Open(name string) (http.File, error) {
|
||||
pt, err := Parse(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pt == f.path {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
pt.Name = path.Join(f.Path().Name, pt.Name)
|
||||
|
||||
di, err := Open(pt.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fi, err := di.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
di.parent = f.path
|
||||
di.excludes = f.excludes
|
||||
}
|
||||
return di, nil
|
||||
}
|
||||
|
||||
func Open(name string) (*File, error) {
|
||||
pt, err := Parse(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f, ok := filesCache.Load(pt)
|
||||
if !ok {
|
||||
return openDisk(pt)
|
||||
}
|
||||
nf := &File{
|
||||
info: WithName(f.info.Name(), f.info),
|
||||
path: f.path,
|
||||
data: f.data,
|
||||
her: f.her,
|
||||
}
|
||||
|
||||
return nf, nil
|
||||
}
|
||||
|
||||
func openDisk(pt Path) (*File, error) {
|
||||
info, err := Info(pt.Pkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fp := info.Dir
|
||||
if len(pt.Name) > 0 {
|
||||
fp = filepath.Join(fp, pt.Name)
|
||||
}
|
||||
|
||||
fi, err := os.Stat(fp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f := &File{
|
||||
info: WithName(strings.TrimPrefix(pt.Name, "/"), NewFileInfo(fi)),
|
||||
her: info,
|
||||
path: pt,
|
||||
}
|
||||
return f, nil
|
||||
}
|
133
open_test.go
133
open_test.go
|
@ -1,133 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_Open_File(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
f, err := Open(".")
|
||||
r.NoError(err)
|
||||
|
||||
ts := httptest.NewServer(http.FileServer(f))
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(ts.URL + "/cmd/pkger/main.go")
|
||||
r.NoError(err)
|
||||
r.Equal(200, res.StatusCode)
|
||||
|
||||
b, err := ioutil.ReadAll(res.Body)
|
||||
r.NoError(err)
|
||||
r.Contains(string(b), "does not compute")
|
||||
|
||||
r.NoError(f.Close())
|
||||
}
|
||||
|
||||
func Test_Open_Dir(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
f, err := Open("/")
|
||||
r.NoError(err)
|
||||
|
||||
ts := httptest.NewServer(http.FileServer(f))
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(ts.URL + "/cmd/pkger")
|
||||
r.NoError(err)
|
||||
r.Equal(200, res.StatusCode)
|
||||
|
||||
b, err := ioutil.ReadAll(res.Body)
|
||||
r.NoError(err)
|
||||
r.Contains(string(b), `<a href="/cmd/pkger/main.go">/cmd/pkger/main.go</a>`)
|
||||
|
||||
r.NoError(f.Close())
|
||||
}
|
||||
|
||||
func Test_Open_File_Memory(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
f, err := Create("/suit/case.txt")
|
||||
r.NoError(err)
|
||||
f.Write([]byte(radio))
|
||||
r.NoError(f.Close())
|
||||
|
||||
r.Equal([]byte(radio), f.data)
|
||||
r.Contains(string(f.data), "I wanna bite the hand that feeds me")
|
||||
|
||||
dir, err := Open("/")
|
||||
r.NoError(err)
|
||||
defer dir.Close()
|
||||
|
||||
ts := httptest.NewServer(http.FileServer(dir))
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(ts.URL + "/suit/case.txt")
|
||||
r.NoError(err)
|
||||
r.Equal(200, res.StatusCode)
|
||||
|
||||
b, _ := ioutil.ReadAll(res.Body)
|
||||
// r.NoError(err)
|
||||
r.Contains(string(b), "I wanna bite the hand that feeds me")
|
||||
|
||||
}
|
||||
|
||||
func Test_Open_Dir_StripPrefix(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
ts := httptest.NewServer(http.StripPrefix("/assets/", http.FileServer(http.Dir("./testdata/public"))))
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(ts.URL + "/assets/radio.radio")
|
||||
r.NoError(err)
|
||||
r.Equal(200, res.StatusCode)
|
||||
|
||||
b, _ := ioutil.ReadAll(res.Body)
|
||||
// r.NoError(err)
|
||||
r.Contains(string(b), "I wanna bite the hand that feeds me")
|
||||
|
||||
res, err = http.Get(ts.URL + "/assets/")
|
||||
r.NoError(err)
|
||||
r.Equal(200, res.StatusCode)
|
||||
|
||||
b, _ = ioutil.ReadAll(res.Body)
|
||||
// r.NoError(err)
|
||||
r.Contains(string(b), `<a href="radio.radio">radio.radio</a>`)
|
||||
}
|
||||
|
||||
func Test_Open_Dir_Memory_StripPrefix(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
err := MkdirAll("/testdata/public", 0755)
|
||||
r.NoError(err)
|
||||
|
||||
dir, err := Open("/testdata/public")
|
||||
r.NoError(err)
|
||||
defer dir.Close()
|
||||
|
||||
ts := httptest.NewServer(http.StripPrefix("/assets/", http.FileServer(dir)))
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(ts.URL + "/assets/radio.radio")
|
||||
r.NoError(err)
|
||||
r.Equal(200, res.StatusCode)
|
||||
|
||||
b, _ := ioutil.ReadAll(res.Body)
|
||||
// r.NoError(err)
|
||||
r.Contains(string(b), "I wanna bite the hand that feeds me")
|
||||
|
||||
res, err = http.Get(ts.URL + "/assets/")
|
||||
r.NoError(err)
|
||||
r.Equal(200, res.StatusCode)
|
||||
|
||||
b, _ = ioutil.ReadAll(res.Body)
|
||||
// r.NoError(err)
|
||||
r.Contains(string(b), `<a href="/radio.radio">/radio.radio</a>`)
|
||||
r.NotContains(string(b), `/public`)
|
||||
r.NotContains(string(b), `//`)
|
||||
}
|
|
@ -9,20 +9,15 @@ import (
|
|||
|
||||
"github.com/markbates/pkger"
|
||||
"github.com/markbates/pkger/here"
|
||||
"github.com/markbates/pkger/pkging"
|
||||
)
|
||||
|
||||
var DefaultIgnoredFolders = []string{".", "_", "vendor", "node_modules", "_fixtures", "testdata"}
|
||||
|
||||
func Parse(name string) (Results, error) {
|
||||
func Parse(cur here.Info) (Results, error) {
|
||||
var r Results
|
||||
c, err := pkger.Stat()
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
name = c.ImportPath
|
||||
}
|
||||
name := cur.ImportPath
|
||||
|
||||
pt, err := pkger.Parse(name)
|
||||
if err != nil {
|
||||
|
@ -36,7 +31,7 @@ func Parse(name string) (Results, error) {
|
|||
return r, err
|
||||
}
|
||||
|
||||
m := map[pkger.Path]bool{}
|
||||
m := map[pkging.Path]bool{}
|
||||
|
||||
root := r.Path.Name
|
||||
if !strings.HasPrefix(root, string(filepath.Separator)) {
|
||||
|
@ -117,7 +112,7 @@ func Parse(name string) (Results, error) {
|
|||
return nil
|
||||
})
|
||||
|
||||
var found []pkger.Path
|
||||
var found []pkging.Path
|
||||
|
||||
for k := range m {
|
||||
if len(k.String()) == 0 {
|
||||
|
@ -133,8 +128,8 @@ func Parse(name string) (Results, error) {
|
|||
return r, err
|
||||
}
|
||||
|
||||
func sourceFiles(pt pkger.Path) ([]pkger.Path, error) {
|
||||
var res []pkger.Path
|
||||
func sourceFiles(pt pkging.Path) ([]pkging.Path, error) {
|
||||
var res []pkging.Path
|
||||
|
||||
her, err := pkger.Info(pt.Pkg)
|
||||
|
||||
|
@ -177,7 +172,7 @@ func sourceFiles(pt pkger.Path) ([]pkger.Path, error) {
|
|||
|
||||
n := strings.TrimPrefix(p, her.Dir)
|
||||
n = strings.Replace(n, "\\", "/", -1)
|
||||
pt := pkger.Path{
|
||||
pt := pkging.Path{
|
||||
Name: n,
|
||||
}
|
||||
res = append(res, pt)
|
||||
|
@ -188,6 +183,6 @@ func sourceFiles(pt pkger.Path) ([]pkger.Path, error) {
|
|||
}
|
||||
|
||||
type Results struct {
|
||||
Paths []pkger.Path
|
||||
Path pkger.Path
|
||||
Paths []pkging.Path
|
||||
Path pkging.Path
|
||||
}
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_Parser(t *testing.T) {
|
||||
r := require.New(t)
|
||||
pwd, err := os.Getwd()
|
||||
r.NoError(err)
|
||||
r.NoError(os.Chdir(filepath.Join("..", "examples", "app")))
|
||||
defer os.Chdir(pwd)
|
||||
|
||||
res, err := Parse("")
|
||||
ch := filepath.Join("..", "examples", "app")
|
||||
info := here.Info{
|
||||
Dir: ch,
|
||||
ImportPath: "github.com/markbates/pkger/examples/app",
|
||||
}
|
||||
res, err := Parse(info)
|
||||
|
||||
r.NoError(err)
|
||||
|
||||
|
|
|
@ -8,11 +8,12 @@ import (
|
|||
|
||||
"github.com/markbates/pkger"
|
||||
"github.com/markbates/pkger/here"
|
||||
"github.com/markbates/pkger/pkging"
|
||||
)
|
||||
|
||||
type visitor struct {
|
||||
File string
|
||||
Found map[pkger.Path]bool
|
||||
Found map[pkging.Path]bool
|
||||
info here.Info
|
||||
errors []error
|
||||
}
|
||||
|
@ -20,12 +21,12 @@ type visitor struct {
|
|||
func newVisitor(p string, info here.Info) (*visitor, error) {
|
||||
return &visitor{
|
||||
File: p,
|
||||
Found: map[pkger.Path]bool{},
|
||||
Found: map[pkging.Path]bool{},
|
||||
info: info,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *visitor) Run() ([]pkger.Path, error) {
|
||||
func (v *visitor) Run() ([]pkging.Path, error) {
|
||||
pf, err := parseFile(v.File)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %v", v.File, err)
|
||||
|
@ -33,7 +34,7 @@ func (v *visitor) Run() ([]pkger.Path, error) {
|
|||
|
||||
ast.Walk(v, pf.Ast)
|
||||
|
||||
var found []pkger.Path
|
||||
var found []pkging.Path
|
||||
|
||||
for k := range v.Found {
|
||||
found = append(found, k)
|
||||
|
|
99
path.go
99
path.go
|
@ -1,99 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Path struct {
|
||||
Pkg string `json:"pkg"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func Parse(p string) (Path, error) {
|
||||
p = strings.Replace(p, "\\", "/", -1)
|
||||
pt, ok := pathsCache.Load(p)
|
||||
if ok {
|
||||
return pt, nil
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return build(p, "", "")
|
||||
}
|
||||
|
||||
res := pathrx.FindAllStringSubmatch(p, -1)
|
||||
if len(res) == 0 {
|
||||
return pt, fmt.Errorf("could not parse %q", p)
|
||||
}
|
||||
|
||||
matches := res[0]
|
||||
|
||||
if len(matches) != 4 {
|
||||
return pt, fmt.Errorf("could not parse %q", p)
|
||||
}
|
||||
|
||||
return build(p, matches[1], matches[3])
|
||||
}
|
||||
|
||||
func (p Path) String() string {
|
||||
if p.Name == "" {
|
||||
p.Name = "/"
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", p.Pkg, p.Name)
|
||||
}
|
||||
|
||||
func (p Path) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if st.Flag('+') {
|
||||
b, err := json.MarshalIndent(p, "", " ")
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(st, string(b))
|
||||
return
|
||||
}
|
||||
fmt.Fprint(st, p.String())
|
||||
case 'q':
|
||||
fmt.Fprintf(st, "%q", p.String())
|
||||
default:
|
||||
fmt.Fprint(st, p.String())
|
||||
}
|
||||
}
|
||||
|
||||
var pathrx = regexp.MustCompile("([^:]+)(:(/.+))?")
|
||||
|
||||
func build(p, pkg, name string) (Path, error) {
|
||||
pt := Path{
|
||||
Pkg: pkg,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
current, err := Stat()
|
||||
if err != nil {
|
||||
return pt, err
|
||||
}
|
||||
|
||||
if strings.HasPrefix(pt.Pkg, "/") || len(pt.Pkg) == 0 {
|
||||
pt.Name = pt.Pkg
|
||||
pt.Pkg = current.ImportPath
|
||||
}
|
||||
|
||||
if len(pt.Name) == 0 {
|
||||
pt.Name = "/"
|
||||
}
|
||||
|
||||
if pt.Pkg == pt.Name {
|
||||
pt.Pkg = current.ImportPath
|
||||
pt.Name = "/"
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(pt.Name, "/") {
|
||||
pt.Name = "/" + pt.Name
|
||||
}
|
||||
pathsCache.Store(p, pt)
|
||||
return pt, nil
|
||||
}
|
52
path_test.go
52
path_test.go
|
@ -1,52 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_Path_String(t *testing.T) {
|
||||
table := []struct {
|
||||
in Path
|
||||
out string
|
||||
}{
|
||||
{in: Path{}, out: ":/"},
|
||||
{in: Path{Pkg: curPkg}, out: curPkg + ":/"},
|
||||
{in: Path{Pkg: curPkg, Name: "/foo.go"}, out: curPkg + ":/foo.go"},
|
||||
{in: Path{Name: "/foo.go"}, out: ":/foo.go"},
|
||||
{in: Path{Pkg: "github.com/markbates/pkger/internal/examples/app"}, out: "github.com/markbates/pkger/internal/examples/app:/"},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
t.Run(tt.in.String(), func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
r.Equal(tt.out, tt.in.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
func Test_Parse(t *testing.T) {
|
||||
table := []struct {
|
||||
in string
|
||||
out Path
|
||||
}{
|
||||
{in: curPkg, out: Path{Pkg: curPkg, Name: "/"}},
|
||||
{in: curPkg + ":/foo.go", out: Path{Pkg: curPkg, Name: "/foo.go"}},
|
||||
{in: "/paths/parse_test.go", out: Path{Pkg: curPkg, Name: "/paths/parse_test.go"}},
|
||||
{in: `\windows\path.go`, out: Path{Pkg: curPkg, Name: "/windows/path.go"}},
|
||||
{in: "", out: Path{Pkg: curPkg, Name: "/"}},
|
||||
{in: "github.com/markbates/pkger/internal/examples/app", out: Path{Pkg: "github.com/markbates/pkger/internal/examples/app", Name: "/"}},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
t.Run(tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pt, err := Parse(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
r.Equal(tt.out.Pkg, pt.Pkg)
|
||||
r.Equal(tt.out.Name, pt.Name)
|
||||
})
|
||||
}
|
||||
}
|
123
paths_map.go
123
paths_map.go
|
@ -1,123 +0,0 @@
|
|||
// Code generated by github.com/gobuffalo/mapgen. DO NOT EDIT.
|
||||
|
||||
package pkger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// pathsMap wraps sync.Map and uses the following types:
|
||||
// key: string
|
||||
// value: Path
|
||||
type pathsMap struct {
|
||||
data *sync.Map
|
||||
once *sync.Once
|
||||
}
|
||||
|
||||
func (m *pathsMap) Data() *sync.Map {
|
||||
if m.once == nil {
|
||||
m.once = &sync.Once{}
|
||||
}
|
||||
m.once.Do(func() {
|
||||
if m.data == nil {
|
||||
m.data = &sync.Map{}
|
||||
}
|
||||
})
|
||||
return m.data
|
||||
}
|
||||
|
||||
func (m *pathsMap) MarshalJSON() ([]byte, error) {
|
||||
mm := map[string]interface{}{}
|
||||
m.Data().Range(func(key, value interface{}) bool {
|
||||
mm[fmt.Sprintf("%s", key)] = value
|
||||
return true
|
||||
})
|
||||
return json.Marshal(mm)
|
||||
}
|
||||
|
||||
func (m *pathsMap) UnmarshalJSON(b []byte) error {
|
||||
mm := map[string]Path{}
|
||||
|
||||
if err := json.Unmarshal(b, &mm); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range mm {
|
||||
m.Store(k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete the key from the map
|
||||
func (m *pathsMap) Delete(key string) {
|
||||
m.Data().Delete(key)
|
||||
}
|
||||
|
||||
// Load the key from the map.
|
||||
// Returns Path or bool.
|
||||
// A false return indicates either the key was not found
|
||||
// or the value is not of type Path
|
||||
func (m *pathsMap) Load(key string) (Path, bool) {
|
||||
i, ok := m.Data().Load(key)
|
||||
if !ok {
|
||||
return Path{}, false
|
||||
}
|
||||
s, ok := i.(Path)
|
||||
return s, ok
|
||||
}
|
||||
|
||||
// LoadOrStore will return an existing key or
|
||||
// store the value if not already in the map
|
||||
func (m *pathsMap) LoadOrStore(key string, value Path) (Path, bool) {
|
||||
i, _ := m.Data().LoadOrStore(key, value)
|
||||
s, ok := i.(Path)
|
||||
return s, ok
|
||||
}
|
||||
|
||||
// LoadOr will return an existing key or
|
||||
// run the function and store the results
|
||||
func (m *pathsMap) LoadOr(key string, fn func(*pathsMap) (Path, bool)) (Path, bool) {
|
||||
i, ok := m.Load(key)
|
||||
if ok {
|
||||
return i, ok
|
||||
}
|
||||
i, ok = fn(m)
|
||||
if ok {
|
||||
m.Store(key, i)
|
||||
return i, ok
|
||||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
// Range over the Path values in the map
|
||||
func (m *pathsMap) Range(f func(key string, value Path) bool) {
|
||||
m.Data().Range(func(k, v interface{}) bool {
|
||||
key, ok := k.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
value, ok := v.(Path)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return f(key, value)
|
||||
})
|
||||
}
|
||||
|
||||
// Store a Path in the map
|
||||
func (m *pathsMap) Store(key string, value Path) {
|
||||
m.Data().Store(key, value)
|
||||
}
|
||||
|
||||
// Keys returns a list of keys in the map
|
||||
func (m *pathsMap) Keys() []string {
|
||||
var keys []string
|
||||
m.Range(func(key string, value Path) bool {
|
||||
keys = append(keys, key)
|
||||
return true
|
||||
})
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
126
pkger.go
126
pkger.go
|
@ -1,117 +1,29 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
"github.com/markbates/pkger/pkging"
|
||||
"github.com/markbates/pkger/pkging/stdos"
|
||||
)
|
||||
|
||||
var filesCache = &filesMap{}
|
||||
var infosCache = &infosMap{}
|
||||
var pathsCache = &pathsMap{}
|
||||
var curOnce = &sync.Once{}
|
||||
var currentInfo here.Info
|
||||
|
||||
var packed bool
|
||||
|
||||
var packMU = &sync.RWMutex{}
|
||||
|
||||
func ReadFile(s string) ([]byte, error) {
|
||||
f, err := Open(s)
|
||||
var current = func() pkging.Pkger {
|
||||
n, err := stdos.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
return ioutil.ReadAll(f)
|
||||
}
|
||||
return n
|
||||
}()
|
||||
|
||||
func Unpack(ind string) error {
|
||||
packed = true
|
||||
packMU.Lock()
|
||||
defer packMU.Unlock()
|
||||
b, err := hex.DecodeString(ind)
|
||||
if err != nil {
|
||||
log.Fatal("hex.DecodeString", err)
|
||||
return err
|
||||
}
|
||||
|
||||
gz, err := gzip.NewReader(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
log.Fatal("gzip.NewReader", err)
|
||||
return err
|
||||
}
|
||||
defer gz.Close()
|
||||
|
||||
var jay jason
|
||||
if err := json.NewDecoder(gz).Decode(&jay); err != nil {
|
||||
return err
|
||||
}
|
||||
jay.Files.Range(func(key Path, value *File) bool {
|
||||
filesCache.Store(key, value)
|
||||
return true
|
||||
})
|
||||
jay.Infos.Range(func(key string, value here.Info) bool {
|
||||
infosCache.Store(key, value)
|
||||
return true
|
||||
})
|
||||
jay.Paths.Range(func(key string, value Path) bool {
|
||||
pathsCache.Store(key, value)
|
||||
return true
|
||||
})
|
||||
currentInfo = jay.CurrentInfo
|
||||
return nil
|
||||
}
|
||||
|
||||
func Pack(out io.Writer, paths []Path) error {
|
||||
packMU.RLock()
|
||||
defer packMU.RUnlock()
|
||||
bb := &bytes.Buffer{}
|
||||
gz := gzip.NewWriter(bb)
|
||||
defer gz.Close()
|
||||
|
||||
for _, p := range paths {
|
||||
f, err := Open(p.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
filesCache.Store(p, f)
|
||||
f.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
filesCache.Store(p, f)
|
||||
f.Close()
|
||||
|
||||
}
|
||||
|
||||
jay := jason{
|
||||
Files: filesCache,
|
||||
Infos: infosCache,
|
||||
Paths: pathsCache,
|
||||
CurrentInfo: currentInfo,
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(gz).Encode(jay); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gz.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
s := hex.EncodeToString(bb.Bytes())
|
||||
_, err := fmt.Fprint(out, s)
|
||||
return err
|
||||
}
|
||||
var Abs = current.Abs
|
||||
var AbsPath = current.AbsPath
|
||||
var Create = current.Create
|
||||
var Current = current.Current
|
||||
var Info = current.Info
|
||||
var MkdirAll = current.MkdirAll
|
||||
var Open = current.Open
|
||||
var Parse = current.Parse
|
||||
var Remove = current.Remove
|
||||
var RemoveAll = current.RemoveAll
|
||||
var Stat = current.Stat
|
||||
var Walk = current.Walk
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const curPkg = "github.com/markbates/pkger"
|
||||
|
||||
func Test_ReadFile(t *testing.T) {
|
||||
r := require.New(t)
|
||||
b, err := ReadFile("/LICENSE")
|
||||
r.NoError(err)
|
||||
r.Contains(string(b), "MIT")
|
||||
}
|
||||
|
||||
func createFile(p string, body ...string) (*File, error) {
|
||||
if len(body) == 0 {
|
||||
body = append(body, radio)
|
||||
}
|
||||
|
||||
f, err := Create(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = io.Copy(f, strings.NewReader(strings.Join(body, "\n\n")))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
const radio = `I was tuning in the shine on the late night dial
|
||||
Doing anything my radio advised
|
||||
With every one of those late night stations
|
||||
Playing songs bringing tears to my eyes
|
||||
I was seriously thinking about hiding the receiver
|
||||
When the switch broke 'cause it's old
|
||||
They're saying things that I can hardly believe
|
||||
They really think we're getting out of control
|
||||
Radio is a sound salvation
|
||||
Radio is cleaning up the nation
|
||||
They say you better listen to the voice of reason
|
||||
But they don't give you any choice 'cause they think that it's treason
|
||||
So you had better do as you are told
|
||||
You better listen to the radio
|
||||
I wanna bite the hand that feeds me
|
||||
I wanna bite that hand so badly
|
||||
I want to make them wish they'd never seen me
|
||||
Some of my friends sit around every evening
|
||||
And they worry about the times ahead
|
||||
But everybody else is overwhelmed by indifference
|
||||
And the promise of an early bed
|
||||
You either shut up or get cut up; they don't wanna hear about it
|
||||
It's only inches on the reel-to-reel
|
||||
And the radio is in the hands of such a lot of fools
|
||||
Tryin' to anesthetize the way that you feel
|
||||
Radio is a sound salvation
|
||||
Radio is cleaning up the nation
|
||||
They say you better listen to the voice of reason
|
||||
But they don't give you any choice 'cause they think that it's treason
|
||||
So you had better do as you are told
|
||||
You better listen to the radio
|
||||
Wonderful radio
|
||||
Marvelous radio
|
||||
Wonderful radio
|
||||
Radio, radio`
|
|
@ -0,0 +1,22 @@
|
|||
package pkging
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
)
|
||||
|
||||
type File interface {
|
||||
Close() error
|
||||
Abs() (string, error)
|
||||
Info() here.Info
|
||||
Name() string
|
||||
Open(name string) (http.File, error)
|
||||
Path() Path
|
||||
Read(p []byte) (int, error)
|
||||
Readdir(count int) ([]os.FileInfo, error)
|
||||
Seek(offset int64, whence int) (int64, error)
|
||||
Stat() (os.FileInfo, error)
|
||||
Write(b []byte) (int, error)
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package pkging
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Details struct {
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
Mode os.FileMode `json:"mode"`
|
||||
ModTime ModTime `json:"mod_time"`
|
||||
IsDir bool `json:"is_dir"`
|
||||
Sys interface{} `json:"sys"`
|
||||
}
|
||||
type FileInfo struct {
|
||||
Details `json:"details"`
|
||||
}
|
||||
|
||||
func (f *FileInfo) String() string {
|
||||
b, _ := json.MarshalIndent(f, "", " ")
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (f *FileInfo) Name() string {
|
||||
return f.Details.Name
|
||||
}
|
||||
|
||||
func (f *FileInfo) Size() int64 {
|
||||
return f.Details.Size
|
||||
}
|
||||
|
||||
func (f *FileInfo) Mode() os.FileMode {
|
||||
return f.Details.Mode
|
||||
}
|
||||
|
||||
func (f *FileInfo) ModTime() time.Time {
|
||||
return time.Time(f.Details.ModTime)
|
||||
}
|
||||
|
||||
func (f *FileInfo) IsDir() bool {
|
||||
return f.Details.IsDir
|
||||
}
|
||||
|
||||
func (f *FileInfo) Sys() interface{} {
|
||||
return f.Details.Sys
|
||||
}
|
||||
|
||||
var _ os.FileInfo = &FileInfo{}
|
||||
|
||||
func NewFileInfo(info os.FileInfo) *FileInfo {
|
||||
fi := &FileInfo{
|
||||
Details: Details{
|
||||
Name: cleanName(info.Name()),
|
||||
Size: info.Size(),
|
||||
Mode: info.Mode(),
|
||||
ModTime: ModTime(info.ModTime()),
|
||||
IsDir: info.IsDir(),
|
||||
Sys: info.Sys(),
|
||||
},
|
||||
}
|
||||
return fi
|
||||
}
|
||||
|
||||
func WithName(name string, info os.FileInfo) *FileInfo {
|
||||
s := cleanName(name)
|
||||
|
||||
if !strings.HasPrefix(s, "/") {
|
||||
s = "/" + s
|
||||
}
|
||||
|
||||
fo := NewFileInfo(info)
|
||||
fo.Details.Name = cleanName(name)
|
||||
return fo
|
||||
}
|
||||
|
||||
func WithRelName(name string, info os.FileInfo) *FileInfo {
|
||||
s := cleanName(name)
|
||||
|
||||
if !strings.HasPrefix(s, "/") {
|
||||
s = "/" + s
|
||||
}
|
||||
|
||||
fo := NewFileInfo(info)
|
||||
fo.Details.Name = cleanName(name)
|
||||
return fo
|
||||
}
|
||||
|
||||
func cleanName(s string) string {
|
||||
if strings.Contains(s, "\\") {
|
||||
s = strings.Replace(s, "\\", "/", -1)
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
package mem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
"github.com/markbates/pkger/pkging"
|
||||
)
|
||||
|
||||
const timeFmt = time.RFC3339Nano
|
||||
|
||||
var _ pkging.File = &File{}
|
||||
|
||||
type File struct {
|
||||
info *pkging.FileInfo
|
||||
her here.Info
|
||||
path pkging.Path
|
||||
data []byte
|
||||
parent pkging.Path
|
||||
writer *bytes.Buffer
|
||||
reader io.Reader
|
||||
pkging pkging.Pkger
|
||||
}
|
||||
|
||||
func (f *File) Seek(ofpkginget int64, whence int) (int64, error) {
|
||||
if sk, ok := f.reader.(io.Seeker); ok {
|
||||
return sk.Seek(ofpkginget, whence)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (f *File) Close() error {
|
||||
defer func() {
|
||||
f.reader = nil
|
||||
f.writer = nil
|
||||
}()
|
||||
if f.reader != nil {
|
||||
if c, ok := f.reader.(io.Closer); ok {
|
||||
if err := c.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if f.writer == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
f.data = f.writer.Bytes()
|
||||
|
||||
fi := f.info
|
||||
fi.Details.Size = int64(len(f.data))
|
||||
fi.Details.ModTime = pkging.ModTime(time.Now())
|
||||
f.info = fi
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) Read(p []byte) (int, error) {
|
||||
if len(f.data) > 0 && f.reader == nil {
|
||||
f.reader = bytes.NewReader(f.data)
|
||||
}
|
||||
|
||||
if f.reader != nil {
|
||||
return f.reader.Read(p)
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("unable to read %s", f.Name())
|
||||
}
|
||||
|
||||
func (f *File) Write(b []byte) (int, error) {
|
||||
if f.writer == nil {
|
||||
f.writer = &bytes.Buffer{}
|
||||
}
|
||||
i, err := f.writer.Write(b)
|
||||
return i, err
|
||||
}
|
||||
|
||||
func (f File) Info() here.Info {
|
||||
return f.her
|
||||
}
|
||||
|
||||
func (f File) Stat() (os.FileInfo, error) {
|
||||
if f.info == nil {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
return f.info, nil
|
||||
}
|
||||
|
||||
func (f File) Name() string {
|
||||
return f.info.Name()
|
||||
}
|
||||
|
||||
func (f File) Abs() (string, error) {
|
||||
return f.pkging.AbsPath(f.Path())
|
||||
}
|
||||
|
||||
func (f File) Path() pkging.Path {
|
||||
return f.path
|
||||
}
|
||||
|
||||
func (f File) String() string {
|
||||
return f.Path().String()
|
||||
}
|
||||
|
||||
// func (f File) Format(st fmt.State, verb rune) {
|
||||
// switch verb {
|
||||
// case 'v':
|
||||
// if st.Flag('+') {
|
||||
// b, err := json.MarshalIndent(f, "", " ")
|
||||
// if err != nil {
|
||||
// fmt.Fprint(os.Stderr, err)
|
||||
// return
|
||||
// }
|
||||
// fmt.Fprint(st, string(b))
|
||||
// return
|
||||
// }
|
||||
// fmt.Fprint(st, f.String())
|
||||
// case 'q':
|
||||
// fmt.Fprintf(st, "%q", f.String())
|
||||
// default:
|
||||
// fmt.Fprint(st, f.String())
|
||||
// }
|
||||
// }
|
||||
|
||||
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
|
||||
var infos []os.FileInfo
|
||||
root := f.Path().String()
|
||||
err := f.pkging.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count > 0 && len(infos) == count {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
pt, err := f.pkging.Parse(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pt.Name == f.parent.Name {
|
||||
return nil
|
||||
}
|
||||
|
||||
info = pkging.WithRelName(strings.TrimPrefix(info.Name(), f.parent.Name), info)
|
||||
infos = append(infos, info)
|
||||
if info.IsDir() && path != root {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*os.PathError); ok {
|
||||
return infos, nil
|
||||
}
|
||||
if err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return infos, nil
|
||||
|
||||
}
|
||||
|
||||
func (f *File) Open(name string) (http.File, error) {
|
||||
pt, err := f.pkging.Parse(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pt == f.path {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
pt.Name = path.Join(f.Path().Name, pt.Name)
|
||||
|
||||
di, err := f.pkging.Open(pt.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fi, err := di.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
d2 := &File{
|
||||
info: pkging.NewFileInfo(fi),
|
||||
her: di.Info(),
|
||||
path: pt,
|
||||
parent: f.path,
|
||||
pkging: f.pkging,
|
||||
}
|
||||
di = d2
|
||||
}
|
||||
return di, nil
|
||||
}
|
||||
|
||||
// func (f File) MarshalJSON() ([]byte, error) {
|
||||
// m := map[string]interface{}{
|
||||
// "info": f.info,
|
||||
// "her": f.her,
|
||||
// "path": f.path,
|
||||
// "data": f.data,
|
||||
// "parent": f.parent,
|
||||
// }
|
||||
// return json.Marshal(m)
|
||||
// }
|
||||
|
||||
// func (f *File) UnmarshalJSON(b []byte) error {
|
||||
// m := map[string]json.RawMessage{}
|
||||
// if err := json.Unmarshal(b, &m); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// info, ok := m["info"]
|
||||
// if !ok {
|
||||
// return fmt.Errorf("missing info")
|
||||
// }
|
||||
//
|
||||
// f.info = &pkging.FileInfo{}
|
||||
// if err := json.Unmarshal(info, f.info); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// her, ok := m["her"]
|
||||
// if !ok {
|
||||
// return fmt.Errorf("missing her")
|
||||
// }
|
||||
// if err := json.Unmarshal(her, &f.her); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// path, ok := m["path"]
|
||||
// if !ok {
|
||||
// return fmt.Errorf("missing path")
|
||||
// }
|
||||
// if err := json.Unmarshal(path, &f.path); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// parent, ok := m["parent"]
|
||||
// if !ok {
|
||||
// return fmt.Errorf("missing parent")
|
||||
// }
|
||||
// if err := json.Unmarshal(parent, &f.parent); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// if err := json.Unmarshal(m["data"], &f.data); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
// }
|
|
@ -0,0 +1,52 @@
|
|||
package mem
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_File_Seek(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
info, err := here.Current()
|
||||
r.NoError(err)
|
||||
|
||||
pkg, err := New(info)
|
||||
r.NoError(err)
|
||||
|
||||
f, err := pkg.Create(":/wilco.band")
|
||||
r.NoError(err)
|
||||
|
||||
data := []byte("a shot in the arm")
|
||||
f.Write(data)
|
||||
r.NoError(f.Close())
|
||||
|
||||
f, err = pkg.Open(":/wilco.band")
|
||||
r.NoError(err)
|
||||
|
||||
b, err := ioutil.ReadAll(f)
|
||||
r.NoError(err)
|
||||
r.Equal(data, b)
|
||||
|
||||
_, err = f.Seek(0, 0)
|
||||
r.NoError(err)
|
||||
|
||||
b, err = ioutil.ReadAll(f)
|
||||
r.NoError(err)
|
||||
r.Equal(data, b)
|
||||
|
||||
b, err = ioutil.ReadAll(f)
|
||||
r.NoError(err)
|
||||
r.NotEqual(data, b)
|
||||
|
||||
_, err = f.Seek(10, 0)
|
||||
r.NoError(err)
|
||||
|
||||
b, err = ioutil.ReadAll(f)
|
||||
r.NoError(err)
|
||||
r.NotEqual(data, b)
|
||||
r.Equal([]byte("the arm"), b)
|
||||
}
|
|
@ -0,0 +1,272 @@
|
|||
package mem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
"github.com/markbates/pkger/internal/maps"
|
||||
"github.com/markbates/pkger/pkging"
|
||||
)
|
||||
|
||||
var _ pkging.Pkger = &Pkger{}
|
||||
|
||||
func WithInfo(fx *Pkger, infos ...here.Info) {
|
||||
for _, info := range infos {
|
||||
fx.infos.Store(info.ImportPath, info)
|
||||
}
|
||||
}
|
||||
|
||||
func New(info here.Info) (*Pkger, error) {
|
||||
f := &Pkger{
|
||||
infos: &maps.Infos{},
|
||||
paths: &maps.Paths{
|
||||
Current: info,
|
||||
},
|
||||
files: &maps.Files{},
|
||||
current: info,
|
||||
}
|
||||
f.infos.Store(info.ImportPath, info)
|
||||
f.MkdirAll("/", 0755)
|
||||
return f, nil
|
||||
}
|
||||
|
||||
type Pkger struct {
|
||||
infos *maps.Infos
|
||||
paths *maps.Paths
|
||||
files *maps.Files
|
||||
current here.Info
|
||||
}
|
||||
|
||||
func (f *Pkger) Abs(p string) (string, error) {
|
||||
pt, err := f.Parse(p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return f.AbsPath(pt)
|
||||
}
|
||||
|
||||
func (f *Pkger) AbsPath(pt pkging.Path) (string, error) {
|
||||
return pt.String(), nil
|
||||
}
|
||||
|
||||
func (f *Pkger) Current() (here.Info, error) {
|
||||
return f.current, nil
|
||||
}
|
||||
|
||||
func (f *Pkger) Info(p string) (here.Info, error) {
|
||||
info, ok := f.infos.Load(p)
|
||||
if !ok {
|
||||
return info, fmt.Errorf("no such package %q", p)
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (f *Pkger) Parse(p string) (pkging.Path, error) {
|
||||
return f.paths.Parse(p)
|
||||
}
|
||||
|
||||
func (fx *Pkger) Remove(name string) error {
|
||||
pt, err := fx.Parse(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := fx.files.Load(pt); !ok {
|
||||
return &os.PathError{"remove", pt.String(), fmt.Errorf("no such file or directory")}
|
||||
}
|
||||
|
||||
fx.files.Delete(pt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fx *Pkger) RemoveAll(name string) error {
|
||||
pt, err := fx.Parse(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fx.files.Range(func(key pkging.Path, file pkging.File) bool {
|
||||
if strings.HasPrefix(key.Name, pt.Name) {
|
||||
fx.files.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
fx.paths.Range(func(key string, value pkging.Path) bool {
|
||||
if strings.HasPrefix(key, pt.Name) {
|
||||
fx.paths.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fx *Pkger) Create(name string) (pkging.File, error) {
|
||||
fx.MkdirAll("/", 0755)
|
||||
pt, err := fx.Parse(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
her, err := fx.Info(pt.Pkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dir := filepath.Dir(pt.Name)
|
||||
if _, err := fx.Stat(dir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f := &File{
|
||||
path: pt,
|
||||
her: her,
|
||||
info: &pkging.FileInfo{
|
||||
Details: pkging.Details{
|
||||
Name: pt.Name,
|
||||
Mode: 0644,
|
||||
ModTime: pkging.ModTime(time.Now()),
|
||||
},
|
||||
},
|
||||
pkging: fx,
|
||||
}
|
||||
|
||||
fx.files.Store(pt, f)
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (fx *Pkger) MkdirAll(p string, perm os.FileMode) error {
|
||||
path, err := fx.Parse(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
root := path.Name
|
||||
|
||||
cur, err := fx.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for root != "" {
|
||||
pt := pkging.Path{
|
||||
Pkg: path.Pkg,
|
||||
Name: root,
|
||||
}
|
||||
if _, ok := fx.files.Load(pt); ok {
|
||||
root = filepath.Dir(root)
|
||||
if root == "/" || root == "\\" {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
f := &File{
|
||||
pkging: fx,
|
||||
path: pt,
|
||||
her: cur,
|
||||
info: &pkging.FileInfo{
|
||||
Details: pkging.Details{
|
||||
Name: pt.Name,
|
||||
Mode: perm,
|
||||
ModTime: pkging.ModTime(time.Now()),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.info.Details.IsDir = true
|
||||
f.info.Details.Mode = perm
|
||||
if err := f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
fx.files.Store(pt, f)
|
||||
root = filepath.Dir(root)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (fx *Pkger) Open(name string) (pkging.File, error) {
|
||||
pt, err := fx.Parse(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fl, ok := fx.files.Load(pt)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not open %s", name)
|
||||
}
|
||||
f, ok := fl.(*File)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not open %s", name)
|
||||
}
|
||||
nf := &File{
|
||||
pkging: fx,
|
||||
info: pkging.WithName(f.info.Name(), f.info),
|
||||
path: f.path,
|
||||
data: f.data,
|
||||
her: f.her,
|
||||
}
|
||||
|
||||
return nf, nil
|
||||
}
|
||||
|
||||
func (fx *Pkger) Stat(name string) (os.FileInfo, error) {
|
||||
pt, err := fx.Parse(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, ok := fx.files.Load(pt)
|
||||
if ok {
|
||||
return f.Stat()
|
||||
}
|
||||
return nil, fmt.Errorf("could not stat %s", pt)
|
||||
}
|
||||
|
||||
func (f *Pkger) Walk(p string, wf filepath.WalkFunc) error {
|
||||
keys := f.files.Keys()
|
||||
|
||||
pt, err := f.Parse(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
skip := "!"
|
||||
|
||||
for _, k := range keys {
|
||||
if !strings.HasPrefix(k.Name, pt.Name) {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(k.Name, skip) {
|
||||
continue
|
||||
}
|
||||
fl, ok := f.files.Load(k)
|
||||
if !ok {
|
||||
return fmt.Errorf("could not find %s", k)
|
||||
}
|
||||
fi, err := fl.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fi = pkging.WithName(strings.TrimPrefix(k.Name, pt.Name), fi)
|
||||
err = wf(k.String(), fi, nil)
|
||||
if err == filepath.SkipDir {
|
||||
|
||||
skip = k.Name
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package mem
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
"github.com/markbates/pkger/pkging"
|
||||
"github.com/markbates/pkger/pkging/pkgtest"
|
||||
)
|
||||
|
||||
func Test_Pkger(t *testing.T) {
|
||||
suite, err := pkgtest.NewSuite("memos", func() (pkging.Pkger, error) {
|
||||
info, err := here.Current()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkg, err := New(info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pkg, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
suite.Test(t)
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package pkging
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
const timeFmt = time.RFC3339Nano
|
||||
|
||||
type ModTime time.Time
|
||||
|
||||
func (m ModTime) MarshalJSON() ([]byte, error) {
|
||||
t := time.Time(m)
|
||||
return json.Marshal(t.Format(timeFmt))
|
||||
}
|
||||
|
||||
func (m *ModTime) UnmarshalJSON(b []byte) error {
|
||||
t := time.Time{}
|
||||
if err := json.Unmarshal(b, &t); err != nil {
|
||||
return err
|
||||
}
|
||||
(*m) = ModTime(t)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package pkging
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Path struct {
|
||||
Pkg string `json:"pkg"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (p Path) String() string {
|
||||
if p.Name == "" {
|
||||
p.Name = "/"
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", p.Pkg, p.Name)
|
||||
}
|
||||
|
||||
func (p Path) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if st.Flag('+') {
|
||||
b, err := json.MarshalIndent(p, "", " ")
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(st, string(b))
|
||||
return
|
||||
}
|
||||
fmt.Fprint(st, p.String())
|
||||
case 'q':
|
||||
fmt.Fprintf(st, "%q", p.String())
|
||||
default:
|
||||
fmt.Fprint(st, p.String())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package pkging
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
)
|
||||
|
||||
type Pkger interface {
|
||||
Parse(p string) (Path, error)
|
||||
Abs(string) (string, error)
|
||||
AbsPath(Path) (string, error)
|
||||
|
||||
Current() (here.Info, error)
|
||||
Info(p string) (here.Info, error)
|
||||
|
||||
// Create creates the named file with mode 0666 (before umask) - It's actually 0644, truncating it if it already exists. If successful, methods on the returned File can be used for I/O; the associated file descriptor has mode O_RDWR.
|
||||
Create(name string) (File, error)
|
||||
|
||||
// MkdirAll creates a directory named path, along with any necessary parents, and returns nil, or else returns an error. The permission bits perm (before umask) are used for all directories that MkdirAll creates. If path is already a directory, MkdirAll does nothing and returns nil.
|
||||
MkdirAll(p string, perm os.FileMode) error
|
||||
|
||||
// Open opens the named file for reading. If successful, methods on the returned file can be used for reading; the associated file descriptor has mode O_RDONLY.
|
||||
Open(name string) (File, error)
|
||||
|
||||
// Stat returns a FileInfo describing the named file.
|
||||
Stat(name string) (os.FileInfo, error)
|
||||
|
||||
// Walk walks the file tree rooted at root, calling walkFn for each file or directory in the tree, including root. All errors that arise visiting files and directories are filtered by walkFn. The files are walked in lexical order, which makes the output deterministic but means that for very large directories Walk can be inefficient. Walk does not follow symbolic links. - That is from the standard library. I know. Their grammar teachers can not be happy with them right now.
|
||||
Walk(p string, wf filepath.WalkFunc) error
|
||||
|
||||
// Remove removes the named file or (empty) directory.
|
||||
Remove(name string) error
|
||||
|
||||
// RemoveAll removes path and any children it contains. It removes everything it can but returns the first error it encounters. If the path does not exist, RemoveAll returns nil (no error).
|
||||
RemoveAll(path string) error
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package pkgtest
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/markbates/pkger/pkging/pkgutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (s Suite) Test_File_Info(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: mould},
|
||||
{in: ":" + mould},
|
||||
{in: ip + ":" + mould},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
r.NoError(pkg.RemoveAll(mould))
|
||||
r.NoError(pkg.MkdirAll(filepath.Dir(tt.in), 0755))
|
||||
err := pkgutil.WriteFile(pkg, tt.in, []byte(mould), 0644)
|
||||
r.NoError(err)
|
||||
|
||||
f, err := pkg.Open(tt.in)
|
||||
r.NoError(err)
|
||||
r.Equal(mould, f.Name())
|
||||
r.Equal(cur.ImportPath, f.Info().ImportPath)
|
||||
r.NoError(f.Close())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// func (s Suite) Test_File_Read(t *testing.T) {
|
||||
// panic("not implemented")
|
||||
// }
|
||||
//
|
||||
// func (s Suite) Test_File_Readdir(t *testing.T) {
|
||||
// panic("not implemented")
|
||||
// }
|
||||
//
|
||||
// func (s Suite) Test_File_Seek(t *testing.T) {
|
||||
// panic("not implemented")
|
||||
// }
|
||||
//
|
||||
// func (s Suite) Test_File_Stat(t *testing.T) {
|
||||
// panic("not implemented")
|
||||
// }
|
||||
//
|
||||
// func (s Suite) Test_File_Write(t *testing.T) {
|
||||
// panic("not implemented")
|
||||
// }
|
|
@ -0,0 +1,221 @@
|
|||
package pkgtest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/markbates/pkger/pkging"
|
||||
"github.com/markbates/pkger/pkging/pkgutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// ├── main.go
|
||||
// ├── public
|
||||
// │ ├── images
|
||||
// │ │ ├── mark.png
|
||||
// │ └── index.html
|
||||
// └── templates
|
||||
// ├── a.txt
|
||||
// └── b
|
||||
// └── b.txt
|
||||
func (s Suite) LoadFolder(pkg pkging.Pkger) error {
|
||||
files := []string{
|
||||
"/main.go",
|
||||
"/public/images/mark.png",
|
||||
"/public/index.html",
|
||||
"/templates/a.txt",
|
||||
"/templates/b/b.txt",
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
if err := pkg.MkdirAll(filepath.Dir(f), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pkgutil.WriteFile(pkg, f, []byte("!"+f), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Suite) Test_HTTP_Dir(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
ip := cur.ImportPath
|
||||
|
||||
table := []struct {
|
||||
in string
|
||||
req string
|
||||
exp string
|
||||
}{
|
||||
{in: "/", req: "/", exp: `>public/</a`},
|
||||
{in: ":" + "/", req: "/", exp: `>public/</a`},
|
||||
{in: ip + ":" + "/", req: "/", exp: `>public/</a`},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in+tt.req, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
r.NoError(s.LoadFolder(pkg))
|
||||
|
||||
dir, err := pkg.Open(tt.in)
|
||||
r.NoError(err)
|
||||
defer dir.Close()
|
||||
|
||||
ts := httptest.NewServer(http.FileServer(dir))
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(ts.URL + tt.req)
|
||||
r.NoError(err)
|
||||
r.Equal(200, res.StatusCode)
|
||||
|
||||
b, err := ioutil.ReadAll(res.Body)
|
||||
r.NoError(err)
|
||||
r.Contains(string(b), tt.exp)
|
||||
r.NotContains(string(b), "mark.png")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s Suite) Test_HTTP_Dir_IndexHTML(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
ip := cur.ImportPath
|
||||
|
||||
table := []struct {
|
||||
in string
|
||||
req string
|
||||
}{
|
||||
{in: "/public", req: "/"},
|
||||
{in: ":" + "/public", req: "/"},
|
||||
{in: ip + ":" + "/public", req: "/"},
|
||||
}
|
||||
|
||||
exp := "index.html"
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in+exp, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
r.NoError(s.LoadFolder(pkg))
|
||||
|
||||
dir, err := pkg.Open(tt.in)
|
||||
r.NoError(err)
|
||||
defer dir.Close()
|
||||
|
||||
ts := httptest.NewServer(http.FileServer(dir))
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(ts.URL + tt.req)
|
||||
r.NoError(err)
|
||||
r.Equal(200, res.StatusCode)
|
||||
|
||||
b, err := ioutil.ReadAll(res.Body)
|
||||
r.NoError(err)
|
||||
|
||||
body := string(b)
|
||||
r.Contains(body, exp)
|
||||
r.NotContains(body, "mark.png")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s Suite) Test_HTTP_File(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
ip := cur.ImportPath
|
||||
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: "/public"},
|
||||
{in: ":" + "/public"},
|
||||
{in: ip + ":" + "/public"},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
r.NoError(s.LoadFolder(pkg))
|
||||
|
||||
dir, err := pkg.Open(tt.in)
|
||||
r.NoError(err)
|
||||
defer dir.Close()
|
||||
|
||||
ts := httptest.NewServer(http.FileServer(dir))
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(ts.URL + "/images/mark.png")
|
||||
r.NoError(err)
|
||||
r.Equal(200, res.StatusCode)
|
||||
|
||||
b, err := ioutil.ReadAll(res.Body)
|
||||
r.NoError(err)
|
||||
|
||||
body := string(b)
|
||||
r.Contains(body, `!/public/images/mark.png`)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// func (s Suite) Test_HTTP_Dir_Memory_StripPrefix(t *testing.T) {
|
||||
// r := require.New(t)
|
||||
//
|
||||
// fs := NewPkger()
|
||||
// r.NoError(Folder.Create(fs))
|
||||
//
|
||||
// dir, err := fs.Open("/public")
|
||||
// r.NoError(err)
|
||||
// defer dir.Close()
|
||||
//
|
||||
// ts := httptest.NewServer(http.StripPrefix("/assets/", http.FileServer(dir)))
|
||||
// defer ts.Close()
|
||||
//
|
||||
// res, err := http.Get(ts.URL + "/assets/images/mark.png")
|
||||
// r.NoError(err)
|
||||
// r.Equal(200, res.StatusCode)
|
||||
//
|
||||
// b, _ := ioutil.ReadAll(res.Body)
|
||||
// // r.NoError(err)
|
||||
// r.Contains(string(b), "!/public/images/mark.png")
|
||||
//
|
||||
// res, err = http.Get(ts.URL + "/assets/images/")
|
||||
// r.NoError(err)
|
||||
// r.Equal(200, res.StatusCode)
|
||||
//
|
||||
// b, _ = ioutil.ReadAll(res.Body)
|
||||
// // r.NoError(err)
|
||||
// r.Contains(string(b), `<a href="/mark.png">/mark.png</a>`)
|
||||
// r.NotContains(string(b), `/public`)
|
||||
// r.NotContains(string(b), `/images`)
|
||||
// r.NotContains(string(b), `/go.mod`)
|
||||
// }
|
|
@ -0,0 +1,603 @@
|
|||
package pkgtest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/markbates/pkger/pkging"
|
||||
"github.com/markbates/pkger/pkging/pkgutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const mould = "/easy/listening/sugar.file"
|
||||
const hart = "/easy/listening/grant.hart"
|
||||
const husker = "github.com/husker/du"
|
||||
|
||||
type Suite struct {
|
||||
Name string
|
||||
gen func() (pkging.Pkger, error)
|
||||
}
|
||||
|
||||
func (s Suite) Make() (pkging.Pkger, error) {
|
||||
if s.gen == nil {
|
||||
return nil, fmt.Errorf("missing generator function")
|
||||
}
|
||||
return s.gen()
|
||||
}
|
||||
|
||||
func NewSuite(name string, fn func() (pkging.Pkger, error)) (Suite, error) {
|
||||
suite := Suite{
|
||||
Name: name,
|
||||
gen: fn,
|
||||
}
|
||||
return suite, nil
|
||||
}
|
||||
|
||||
func (s Suite) Test(t *testing.T) {
|
||||
rv := reflect.ValueOf(s)
|
||||
rt := rv.Type()
|
||||
if rt.NumMethod() == 0 {
|
||||
t.Fatalf("something went wrong wrong with %s", s.Name)
|
||||
}
|
||||
for i := 0; i < rt.NumMethod(); i++ {
|
||||
m := rt.Method(i)
|
||||
if !strings.HasPrefix(m.Name, "Test_") {
|
||||
continue
|
||||
}
|
||||
|
||||
s.sub(t, m)
|
||||
}
|
||||
}
|
||||
|
||||
// func (s Suite) clone() (Suite, error) {
|
||||
// if ns, ok := s.Pkger.(Newable); ok {
|
||||
// pkg, err := ns.New()
|
||||
// if err != nil {
|
||||
// return s, err
|
||||
// }
|
||||
// s, err = NewSuite(pkg)
|
||||
// if err != nil {
|
||||
// return s, err
|
||||
// }
|
||||
// }
|
||||
// if ns, ok := s.Pkger.(WithRootable); ok {
|
||||
// dir, err := ioutil.TempDir("")
|
||||
// if err != nil {
|
||||
// return s, err
|
||||
// }
|
||||
// // defer opkg.RemoveAll(dir)
|
||||
//
|
||||
// pkg, err := ns.WithRoot(dir)
|
||||
// if err != nil {
|
||||
// return s, err
|
||||
// }
|
||||
// s, err = NewSuite(pkg)
|
||||
// if err != nil {
|
||||
// return s, err
|
||||
// }
|
||||
// }
|
||||
// return s, nil
|
||||
// }
|
||||
|
||||
func (s Suite) Run(t *testing.T, name string, fn func(t *testing.T)) {
|
||||
t.Run(name, func(st *testing.T) {
|
||||
fn(st)
|
||||
})
|
||||
}
|
||||
|
||||
func (s Suite) sub(t *testing.T, m reflect.Method) {
|
||||
name := fmt.Sprintf("%s/%s", s.Name, m.Name)
|
||||
// s, err := s.clone()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
s.Run(t, name, func(st *testing.T) {
|
||||
m.Func.Call([]reflect.Value{
|
||||
reflect.ValueOf(s),
|
||||
reflect.ValueOf(st),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (s Suite) Test_Create(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: mould},
|
||||
{in: ":" + mould},
|
||||
{in: ip + ":" + mould},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pt, err := pkg.Parse(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
r.NoError(pkg.MkdirAll(filepath.Dir(pt.Name), 0755))
|
||||
|
||||
f, err := pkg.Create(pt.Name)
|
||||
r.NoError(err)
|
||||
r.Equal(pt.Name, f.Name())
|
||||
|
||||
fi, err := f.Stat()
|
||||
r.NoError(err)
|
||||
r.NoError(f.Close())
|
||||
|
||||
r.Equal(pt.Name, fi.Name())
|
||||
r.NotZero(fi.ModTime())
|
||||
r.NoError(pkg.RemoveAll(pt.String()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s Suite) Test_Create_No_MkdirAll(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: mould},
|
||||
{in: ":" + mould},
|
||||
{in: ip + ":" + mould},
|
||||
{in: filepath.Dir(mould)},
|
||||
{in: ":" + filepath.Dir(mould)},
|
||||
{in: ip + ":" + filepath.Dir(mould)},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
pt, err := pkg.Parse(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
_, err = pkg.Create(pt.Name)
|
||||
r.Error(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s Suite) Test_Current(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
info, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
r.NotZero(info)
|
||||
}
|
||||
|
||||
func (s Suite) Test_Info(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
info, err := pkg.Info(cur.ImportPath)
|
||||
r.NoError(err)
|
||||
r.NotZero(info)
|
||||
|
||||
}
|
||||
|
||||
func (s Suite) Test_MkdirAll(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: mould},
|
||||
{in: ":" + mould},
|
||||
{in: ip + ":" + mould},
|
||||
{in: filepath.Dir(mould)},
|
||||
{in: ":" + filepath.Dir(mould)},
|
||||
{in: ip + ":" + filepath.Dir(mould)},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
pt, err := pkg.Parse(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
dir := filepath.Dir(pt.Name)
|
||||
r.NoError(pkg.MkdirAll(dir, 0755))
|
||||
|
||||
fi, err := pkg.Stat(dir)
|
||||
r.NoError(err)
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
dir = strings.Replace(dir, "\\", "/", -1)
|
||||
}
|
||||
r.Equal(dir, fi.Name())
|
||||
r.NotZero(fi.ModTime())
|
||||
r.NoError(pkg.RemoveAll(pt.String()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s Suite) Test_Open_File(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: mould},
|
||||
{in: ":" + mould},
|
||||
{in: ip + ":" + mould},
|
||||
{in: hart},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
pt, err := pkg.Parse(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
r.NoError(pkg.RemoveAll(pt.String()))
|
||||
r.NoError(pkg.MkdirAll(filepath.Dir(pt.Name), 0755))
|
||||
|
||||
body := "!" + pt.String()
|
||||
|
||||
pkgutil.WriteFile(pkg, tt.in, []byte(body), 0644)
|
||||
|
||||
f, err := pkg.Open(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
r.Equal(pt.Name, f.Path().Name)
|
||||
b, err := ioutil.ReadAll(f)
|
||||
r.NoError(err)
|
||||
r.Equal(body, string(b))
|
||||
|
||||
b, err = pkgutil.ReadFile(pkg, tt.in)
|
||||
r.NoError(err)
|
||||
r.Equal(body, string(b))
|
||||
|
||||
r.NoError(f.Close())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s Suite) Test_Parse(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
table := []struct {
|
||||
in string
|
||||
exp pkging.Path
|
||||
}{
|
||||
{in: mould, exp: pkging.Path{Pkg: ip, Name: mould}},
|
||||
{in: filepath.Join(cur.Dir, mould), exp: pkging.Path{Pkg: ip, Name: mould}},
|
||||
{in: ":" + mould, exp: pkging.Path{Pkg: ip, Name: mould}},
|
||||
{in: ip + ":" + mould, exp: pkging.Path{Pkg: ip, Name: mould}},
|
||||
{in: ip, exp: pkging.Path{Pkg: ip, Name: "/"}},
|
||||
{in: ":", exp: pkging.Path{Pkg: ip, Name: "/"}},
|
||||
{in: husker + ":" + mould, exp: pkging.Path{Pkg: husker, Name: mould}},
|
||||
{in: husker, exp: pkging.Path{Pkg: husker, Name: "/"}},
|
||||
{in: husker + ":", exp: pkging.Path{Pkg: husker, Name: "/"}},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pt, err := pkg.Parse(tt.in)
|
||||
r.NoError(err)
|
||||
r.Equal(tt.exp, pt)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s Suite) Test_Stat_Error(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: hart},
|
||||
{in: ":" + hart},
|
||||
{in: ip},
|
||||
{in: ip + ":"},
|
||||
{in: ip + ":" + hart},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
|
||||
r := require.New(st)
|
||||
|
||||
pt, err := pkg.Parse(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
r.NoError(pkg.RemoveAll(pt.String()))
|
||||
|
||||
_, err = pkg.Stat(tt.in)
|
||||
r.Error(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s Suite) Test_Stat_Dir(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
dir := filepath.Dir(mould)
|
||||
ip := cur.ImportPath
|
||||
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: ip},
|
||||
{in: dir},
|
||||
{in: ":" + dir},
|
||||
{in: ip + ":" + dir},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
|
||||
r := require.New(st)
|
||||
|
||||
pt, err := pkg.Parse(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
r.NoError(pkg.RemoveAll(pt.String()))
|
||||
|
||||
r.NoError(pkg.MkdirAll(pt.Name, 0755))
|
||||
info, err := pkg.Stat(tt.in)
|
||||
r.NoError(err)
|
||||
r.Equal(pt.Name, info.Name())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s Suite) Test_Stat_File(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: mould},
|
||||
{in: ":" + mould},
|
||||
{in: ip + ":" + mould},
|
||||
{in: hart},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
|
||||
r := require.New(st)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
pt, err := pkg.Parse(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
r.NoError(pkg.RemoveAll(pt.String()))
|
||||
r.NoError(pkg.MkdirAll(filepath.Dir(pt.Name), 0755))
|
||||
|
||||
f, err := pkg.Create(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
_, err = io.Copy(f, strings.NewReader("!"+pt.String()))
|
||||
r.NoError(err)
|
||||
r.NoError(f.Close())
|
||||
|
||||
info, err := pkg.Stat(tt.in)
|
||||
r.NoError(err)
|
||||
r.Equal(pt.Name, info.Name())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s Suite) Test_Walk(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
r.NoError(s.LoadFolder(pkg))
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: ip},
|
||||
{in: "/"},
|
||||
{in: ":/"},
|
||||
{in: ip + ":/"},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
var act []string
|
||||
err := pkg.Walk(tt.in, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
act = append(act, path)
|
||||
return nil
|
||||
})
|
||||
r.NoError(err)
|
||||
|
||||
exp := []string{
|
||||
"github.com/markbates/pkger:/",
|
||||
"github.com/markbates/pkger:/main.go",
|
||||
"github.com/markbates/pkger:/public",
|
||||
"github.com/markbates/pkger:/public/images",
|
||||
"github.com/markbates/pkger:/public/images/mark.png",
|
||||
"github.com/markbates/pkger:/public/index.html",
|
||||
"github.com/markbates/pkger:/templates",
|
||||
"github.com/markbates/pkger:/templates/a.txt",
|
||||
"github.com/markbates/pkger:/templates/b",
|
||||
"github.com/markbates/pkger:/templates/b/b.txt",
|
||||
}
|
||||
r.Equal(exp, act)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s Suite) Test_Remove(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: "/public/images/mark.png"},
|
||||
{in: ":/public/images/mark.png"},
|
||||
{in: ip + ":/public/images/mark.png"},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
r.NoError(s.LoadFolder(pkg))
|
||||
|
||||
_, err = pkg.Stat(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
r.NoError(pkg.Remove(tt.in))
|
||||
|
||||
_, err = pkg.Stat(tt.in)
|
||||
r.Error(err)
|
||||
|
||||
r.Error(pkg.Remove("unknown"))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s Suite) Test_RemoveAll(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: "/public"},
|
||||
{in: ":/public"},
|
||||
{in: ip + ":/public"},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
r.NoError(s.LoadFolder(pkg))
|
||||
|
||||
_, err = pkg.Stat(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
r.NoError(pkg.RemoveAll(tt.in))
|
||||
|
||||
_, err = pkg.Stat(tt.in)
|
||||
r.Error(err)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package pkgtest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/markbates/pkger/pkging/pkgutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (s Suite) Test_Util_ReadFile(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
cur, err := pkg.Current()
|
||||
r.NoError(err)
|
||||
|
||||
ip := cur.ImportPath
|
||||
table := []struct {
|
||||
in string
|
||||
}{
|
||||
{in: mould},
|
||||
{in: ":" + mould},
|
||||
{in: ip + ":" + mould},
|
||||
{in: hart},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
s.Run(t, tt.in, func(st *testing.T) {
|
||||
r := require.New(st)
|
||||
|
||||
pkg, err := s.Make()
|
||||
r.NoError(err)
|
||||
|
||||
pt, err := pkg.Parse(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
r.NoError(pkg.RemoveAll(pt.String()))
|
||||
r.NoError(pkg.MkdirAll(filepath.Dir(pt.Name), 0755))
|
||||
|
||||
f, err := pkg.Create(tt.in)
|
||||
r.NoError(err)
|
||||
|
||||
body := "!" + pt.String()
|
||||
_, err = io.Copy(f, strings.NewReader(body))
|
||||
r.NoError(err)
|
||||
r.NoError(f.Close())
|
||||
|
||||
b, err := pkgutil.ReadFile(pkg, tt.in)
|
||||
r.NoError(err)
|
||||
r.Equal(body, string(b))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package pkgutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/markbates/pkger/pkging"
|
||||
)
|
||||
|
||||
type Opener interface {
|
||||
Open(name string) (pkging.File, error)
|
||||
}
|
||||
|
||||
type Creator interface {
|
||||
Create(name string) (pkging.File, error)
|
||||
}
|
||||
|
||||
type OpenFiler interface {
|
||||
OpenFile(name string, flag int, perm os.FileMode) (pkging.File, error)
|
||||
}
|
||||
|
||||
// ReadDir reads the directory named by dirname and returns a list of directory entries sorted by filename.
|
||||
func ReadDir(pkg Opener, dirname string) ([]os.FileInfo, error) {
|
||||
f, err := pkg.Open(dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := f.Readdir(-1)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// ReadFile reads the file named by filename and returns the contents. A successful call returns err == nil, not err == EOF. Because ReadFile reads the whole file, it does not treat an EOF from Read as an error to be reported.
|
||||
func ReadFile(pkg Opener, s string) ([]byte, error) {
|
||||
f, err := pkg.Open(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return ioutil.ReadAll(f)
|
||||
}
|
||||
|
||||
// WriteFile writes data to a file named by filename. If the file does not exist, WriteFile creates it with permissions perm; otherwise WriteFile truncates it before writing.
|
||||
func WriteFile(pkg Creator, filename string, data []byte, perm os.FileMode) error {
|
||||
var f pkging.File
|
||||
var err error
|
||||
|
||||
if of, ok := pkg.(OpenFiler); ok {
|
||||
f, err = of.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if f == nil {
|
||||
f, err = pkg.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
n, err := f.Write(data)
|
||||
if err == nil && n < len(data) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
if err1 := f.Close(); err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package stdos
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
"github.com/markbates/pkger/pkging"
|
||||
)
|
||||
|
||||
var _ pkging.File = &File{}
|
||||
|
||||
type File struct {
|
||||
*os.File
|
||||
info *pkging.FileInfo
|
||||
her here.Info
|
||||
path pkging.Path
|
||||
pkging pkging.Pkger
|
||||
}
|
||||
|
||||
func NewFile(fx pkging.Pkger, osf *os.File) (*File, error) {
|
||||
name := osf.Name()
|
||||
pt, err := fx.Parse(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info, err := osf.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f := &File{
|
||||
File: osf,
|
||||
path: pt,
|
||||
pkging: fx,
|
||||
}
|
||||
f.info = pkging.WithName(pt.Name, info)
|
||||
|
||||
her, err := here.Package(pt.Pkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.her = her
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *File) Close() error {
|
||||
return f.File.Close()
|
||||
}
|
||||
|
||||
func (f *File) Abs() (string, error) {
|
||||
return f.pkging.AbsPath(f.path)
|
||||
}
|
||||
|
||||
func (f *File) Info() here.Info {
|
||||
return f.her
|
||||
}
|
||||
|
||||
func (f *File) Name() string {
|
||||
return f.info.Name()
|
||||
}
|
||||
|
||||
type HTTP struct {
|
||||
pkging.File
|
||||
osf *os.File
|
||||
}
|
||||
|
||||
func (f *HTTP) Readdir(count int) ([]os.FileInfo, error) {
|
||||
osinfos, err := f.osf.Readdir(count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
infos := make([]os.FileInfo, len(osinfos))
|
||||
for i, info := range osinfos {
|
||||
infos[i] = pkging.WithName(info.Name(), info)
|
||||
}
|
||||
return infos, err
|
||||
}
|
||||
|
||||
func (f *File) Open(name string) (http.File, error) {
|
||||
fp := path.Join(f.Path().Name, name)
|
||||
f2, err := f.pkging.Open(fp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &HTTP{File: f2, osf: f.File}, nil
|
||||
}
|
||||
|
||||
func (f *File) Path() pkging.Path {
|
||||
return f.path
|
||||
}
|
||||
|
||||
func (f *File) Stat() (os.FileInfo, error) {
|
||||
if f.info != nil {
|
||||
return f.info, nil
|
||||
}
|
||||
|
||||
nf, err := NewFile(f.pkging, f.File)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
(*f) = *nf
|
||||
return f.info, nil
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package stdos
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_File_Stat_No_Info(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
pkg, err := New()
|
||||
r.NoError(err)
|
||||
|
||||
f, err := pkg.Open(":/pkging/stdos/file_test.go")
|
||||
r.NoError(err)
|
||||
defer f.Close()
|
||||
|
||||
sf, ok := f.(*File)
|
||||
r.True(ok)
|
||||
|
||||
oi := sf.info
|
||||
sf.info = nil
|
||||
|
||||
info, err := sf.Stat()
|
||||
r.NoError(err)
|
||||
r.Equal(oi.Name(), info.Name())
|
||||
// r.Equal("", f.Name())
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
package stdos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
"github.com/markbates/pkger/internal/maps"
|
||||
"github.com/markbates/pkger/pkging"
|
||||
)
|
||||
|
||||
var _ pkging.Pkger = &Pkger{}
|
||||
|
||||
type Pkger struct {
|
||||
infos *maps.Infos
|
||||
paths *maps.Paths
|
||||
current here.Info
|
||||
}
|
||||
|
||||
func (f *Pkger) Abs(p string) (string, error) {
|
||||
pt, err := f.Parse(p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return f.AbsPath(pt)
|
||||
}
|
||||
|
||||
func (f *Pkger) AbsPath(pt pkging.Path) (string, error) {
|
||||
if pt.Pkg == f.current.ImportPath {
|
||||
return filepath.Join(f.current.Dir, pt.Name), nil
|
||||
}
|
||||
info, err := f.Info(pt.Pkg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(info.Dir, pt.Name), nil
|
||||
}
|
||||
|
||||
func New() (*Pkger, error) {
|
||||
info, err := here.Current()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := &Pkger{
|
||||
infos: &maps.Infos{},
|
||||
paths: &maps.Paths{
|
||||
Current: info,
|
||||
},
|
||||
current: info,
|
||||
}
|
||||
p.infos.Store(info.ImportPath, info)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (fx *Pkger) Create(name string) (pkging.File, error) {
|
||||
name, err := fx.Abs(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Create(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewFile(fx, f)
|
||||
}
|
||||
|
||||
func (f *Pkger) Current() (here.Info, error) {
|
||||
return f.current, nil
|
||||
}
|
||||
|
||||
func (f *Pkger) Info(p string) (here.Info, error) {
|
||||
info, ok := f.infos.Load(p)
|
||||
if ok {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
info, err := here.Package(p)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
f.infos.Store(p, info)
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (f *Pkger) MkdirAll(p string, perm os.FileMode) error {
|
||||
p, err := f.Abs(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.MkdirAll(p, perm)
|
||||
}
|
||||
|
||||
func (fx *Pkger) Open(name string) (pkging.File, error) {
|
||||
name, err := fx.Abs(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewFile(fx, f)
|
||||
}
|
||||
|
||||
func (f *Pkger) Parse(p string) (pkging.Path, error) {
|
||||
return f.paths.Parse(p)
|
||||
}
|
||||
|
||||
func (f *Pkger) Stat(name string) (os.FileInfo, error) {
|
||||
pt, err := f.Parse(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
abs, err := f.AbsPath(pt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, err := os.Stat(abs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info = pkging.WithName(pt.Name, pkging.NewFileInfo(info))
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (f *Pkger) Walk(p string, wf filepath.WalkFunc) error {
|
||||
fp, err := f.Abs(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pt, err := f.Parse(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = filepath.Walk(fp, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path = strings.TrimPrefix(path, fp)
|
||||
pt, err := f.Parse(fmt.Sprintf("%s:%s", pt.Pkg, path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return wf(pt.String(), pkging.WithName(path, pkging.NewFileInfo(fi)), nil)
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (fx *Pkger) Remove(name string) error {
|
||||
name, err := fx.Abs(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Remove(name)
|
||||
}
|
||||
|
||||
func (fx *Pkger) RemoveAll(name string) error {
|
||||
name, err := fx.Abs(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.RemoveAll(name)
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package stdos
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/markbates/pkger/pkging"
|
||||
"github.com/markbates/pkger/pkging/pkgtest"
|
||||
)
|
||||
|
||||
func Test_Pkger(t *testing.T) {
|
||||
suite, err := pkgtest.NewSuite("stdos", func() (pkging.Pkger, error) {
|
||||
mypkging, err := New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "stdos")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mypkging.current.Dir = dir
|
||||
mypkging.paths.Current = mypkging.current
|
||||
|
||||
return mypkging, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
suite.Test(t)
|
||||
}
|
14
stat.go
14
stat.go
|
@ -1,14 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import "github.com/markbates/pkger/here"
|
||||
|
||||
func Stat() (here.Info, error) {
|
||||
var err error
|
||||
curOnce.Do(func() {
|
||||
if currentInfo.IsZero() {
|
||||
currentInfo, err = here.Current()
|
||||
}
|
||||
})
|
||||
|
||||
return currentInfo, err
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
I was tuning in the shine on the late night dial
|
||||
Doing anything my radio advised
|
||||
With every one of those late night stations
|
||||
Playing songs bringing tears to my eyes
|
||||
I was seriously thinking about hiding the receiver
|
||||
When the switch broke 'cause it's old
|
||||
They're saying things that I can hardly believe
|
||||
They really think we're getting out of control
|
||||
Radio is a sound salvation
|
||||
Radio is cleaning up the nation
|
||||
They say you better listen to the voice of reason
|
||||
But they don't give you any choice 'cause they think that it's treason
|
||||
So you had better do as you are told
|
||||
You better listen to the radio
|
||||
I wanna bite the hand that feeds me
|
||||
I wanna bite that hand so badly
|
||||
I want to make them wish they'd never seen me
|
||||
Some of my friends sit around every evening
|
||||
And they worry about the times ahead
|
||||
But everybody else is overwhelmed by indifference
|
||||
And the promise of an early bed
|
||||
You either shut up or get cut up; they don't wanna hear about it
|
||||
It's only inches on the reel-to-reel
|
||||
And the radio is in the hands of such a lot of fools
|
||||
Tryin' to anesthetize the way that you feel
|
||||
Radio is a sound salvation
|
||||
Radio is cleaning up the nation
|
||||
They say you better listen to the voice of reason
|
||||
But they don't give you any choice 'cause they think that it's treason
|
||||
So you had better do as you are told
|
||||
You better listen to the radio
|
||||
Wonderful radio
|
||||
Marvelous radio
|
||||
Wonderful radio
|
||||
Radio, radio
|
|
@ -1,4 +0,0 @@
|
|||
package pkger
|
||||
|
||||
// Version of pkger
|
||||
const Version = "v0.0.1"
|
70
walk.go
70
walk.go
|
@ -1,70 +0,0 @@
|
|||
package pkger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/markbates/pkger/here"
|
||||
)
|
||||
|
||||
type WalkFunc func(Path, os.FileInfo) error
|
||||
|
||||
func Walk(p string, wf WalkFunc) error {
|
||||
pt, err := Parse(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filesCache.Range(func(k Path, v *File) bool {
|
||||
if k.Pkg != pt.Pkg {
|
||||
return true
|
||||
}
|
||||
if !strings.HasPrefix(k.Name, pt.Name) {
|
||||
return true
|
||||
}
|
||||
if err = wf(k, v.info); err != nil {
|
||||
if err == filepath.SkipDir {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if packed {
|
||||
return nil
|
||||
}
|
||||
|
||||
var info here.Info
|
||||
if pt.Pkg == "." {
|
||||
info, err = Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pt.Pkg = info.ImportPath
|
||||
}
|
||||
|
||||
if info.IsZero() {
|
||||
info, err = Info(pt.Pkg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s", pt, err)
|
||||
}
|
||||
}
|
||||
fp := filepath.Join(info.Dir, pt.Name)
|
||||
err = filepath.Walk(fp, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path = strings.TrimPrefix(path, info.Dir)
|
||||
pt, err := Parse(fmt.Sprintf("%s:%s", pt.Pkg, path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return wf(pt, WithName(path, NewFileInfo(fi)))
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
Loading…
Reference in New Issue