diff --git a/create.go b/create.go new file mode 100644 index 0000000..2adae16 --- /dev/null +++ b/create.go @@ -0,0 +1,36 @@ +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 +} diff --git a/create_test.go b/create_test.go new file mode 100644 index 0000000..79caf2a --- /dev/null +++ b/create_test.go @@ -0,0 +1,55 @@ +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()) +} diff --git a/current.go b/current.go deleted file mode 100644 index df6d462..0000000 --- a/current.go +++ /dev/null @@ -1 +0,0 @@ -package pkger diff --git a/index.go b/index.go deleted file mode 100644 index 8a6aefa..0000000 --- a/index.go +++ /dev/null @@ -1,126 +0,0 @@ -package pkger - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "sync" - "time" - - "github.com/markbates/pkger/here" - "github.com/markbates/pkger/internal/debug" -) - -var filesCache = &filesMap{} -var infosCache = &infosMap{} -var pathsCache = &pathsMap{} -var curOnce = &sync.Once{} -var currentInfo here.Info - -func dubeg(key, format string, args ...interface{}) { - s := fmt.Sprintf(format, args...) - debug.Debug("[%s|%s] %s", key, s) -} - -func Parse(p string) (Path, error) { - dubeg("Parse", p) - 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 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 -} - -func Stat() (here.Info, error) { - var err error - curOnce.Do(func() { - if currentInfo.IsZero() { - currentInfo, err = here.Current() - } - }) - - return currentInfo, err -} - -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 -} - -func openDisk(pt Path) (*File, error) { - dubeg("openDisk", pt.String()) - 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 -} diff --git a/index_test.go b/index_test.go deleted file mode 100644 index e0e3351..0000000 --- a/index_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package pkger - -import ( - "io" - "os" - "strings" - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_index_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 Test_index_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()) -} - -// TODO -// func Test_index_JSON(t *testing.T) { -// r := require.New(t) -// -// f, err := Create("/radio.radio") -// r.NoError(err) -// r.NotNil(f) -// fmt.Fprint(f, radio) -// r.NoError(f.Close()) -// -// c, err := Stat() -// r.NoError(err) -// r.Equal(curPkg, c.ImportPath) -// -// _, err = Info("github.com/markbates/hepa") -// r.NoError(err) -// -// r.Equal(1, len(filesCache.Keys())) -// r.Equal(1, len(infosCache.Keys())) -// r.NotZero(cur) -// -// jason, err := json.Marshal(i) -// r.NoError(err) -// r.NotZero(jason) -// -// i2 := &index{} -// -// r.NoError(json.Unmarshal(jason, i2)) -// -// r.NotNil(i2.infosCache) -// r.NotNil(i2.filesCache) -// r.NotZero(i2.cur) -// r.Equal(1, len(i2.filesCache.Keys())) -// r.Equal(1, len(i2.infosCache.Keys())) -// -// f2, err := i2.Open(Path{Name: "/radio.radio"}) -// r.NoError(err) -// r.Equal(f.data, f2.data) -// } - -func Test_index_Parse(t *testing.T) { - table := []struct { - in string - out string - }{ - {in: "", out: curPkg + ":/"}, - {in: curPkg, out: curPkg + ":/"}, - // {in: curPkg + "/foo.go", out: curPkg + ":/foo.go"}, - // {in: "/foo.go", out: curPkg + ":/foo.go"}, - {in: "github.com/markbates/pkger/internal/examples/app", out: "github.com/markbates/pkger/internal/examples/app:/"}, - } - - 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, pt.String()) - }) - } -} diff --git a/info.go b/info.go new file mode 100644 index 0000000..4f52457 --- /dev/null +++ b/info.go @@ -0,0 +1,17 @@ +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 +} diff --git a/http.go b/open.go similarity index 63% rename from http.go rename to open.go index c25730f..46cde3b 100644 --- a/http.go +++ b/open.go @@ -2,7 +2,10 @@ package pkger import ( "net/http" + "os" "path" + "path/filepath" + "strings" ) func (f *File) Open(name string) (http.File, error) { @@ -52,3 +55,26 @@ func Open(name string) (*File, error) { return nf, nil } + +func openDisk(pt Path) (*File, error) { + dubeg("openDisk", pt.String()) + 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 +} diff --git a/http_test.go b/open_test.go similarity index 93% rename from http_test.go rename to open_test.go index 560f521..e3fa2e9 100644 --- a/http_test.go +++ b/open_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/require" ) -func Test_HTTP_File(t *testing.T) { +func Test_Open_File(t *testing.T) { r := require.New(t) f, err := Open(".") @@ -29,7 +29,7 @@ func Test_HTTP_File(t *testing.T) { r.NoError(f.Close()) } -func Test_HTTP_Dir(t *testing.T) { +func Test_Open_Dir(t *testing.T) { r := require.New(t) f, err := Open("/") @@ -49,7 +49,7 @@ func Test_HTTP_Dir(t *testing.T) { r.NoError(f.Close()) } -func Test_HTTP_File_Memory(t *testing.T) { +func Test_Open_File_Memory(t *testing.T) { r := require.New(t) f, err := Create("/suit/case.txt") @@ -77,7 +77,7 @@ func Test_HTTP_File_Memory(t *testing.T) { } -func Test_HTTP_Dir_Memory(t *testing.T) { +func Test_Open_Dir_Memory(t *testing.T) { r := require.New(t) f, err := Create("/public/radio.radio") diff --git a/parse.go b/parse.go deleted file mode 100644 index 2909f8f..0000000 --- a/parse.go +++ /dev/null @@ -1,40 +0,0 @@ -package pkger - -import ( - "regexp" - "strings" -) - -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 -} diff --git a/parse_test.go b/parse_test.go deleted file mode 100644 index d0d089d..0000000 --- a/parse_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package pkger - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_Parse_Happy(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: "", out: Path{Pkg: curPkg, 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) - }) - } -} diff --git a/path.go b/path.go index 5349e76..8bb37fa 100644 --- a/path.go +++ b/path.go @@ -4,6 +4,8 @@ import ( "encoding/json" "fmt" "os" + "regexp" + "strings" ) type Path struct { @@ -11,6 +13,31 @@ type Path struct { Name string `json:"name"` } +func Parse(p string) (Path, error) { + dubeg("Parse", p) + 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 = "/" @@ -37,3 +64,37 @@ func (p Path) Format(st fmt.State, verb rune) { 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 +} diff --git a/path_test.go b/path_test.go index 4582b7f..5366177 100644 --- a/path_test.go +++ b/path_test.go @@ -25,3 +25,28 @@ func Test_Path_String(t *testing.T) { }) } } +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) + }) + } +} diff --git a/pkger.go b/pkger.go index 80014bd..6db64da 100644 --- a/pkger.go +++ b/pkger.go @@ -8,8 +8,23 @@ import ( "fmt" "io" "log" + "sync" + + "github.com/markbates/pkger/here" + "github.com/markbates/pkger/internal/debug" ) +var filesCache = &filesMap{} +var infosCache = &infosMap{} +var pathsCache = &pathsMap{} +var curOnce = &sync.Once{} +var currentInfo here.Info + +func dubeg(key, format string, args ...interface{}) { + s := fmt.Sprintf(format, args...) + debug.Debug("[%s|%s] %s", key, s) +} + func Unpack(ind string) error { b, err := hex.DecodeString(ind) if err != nil { diff --git a/stat.go b/stat.go new file mode 100644 index 0000000..d76c264 --- /dev/null +++ b/stat.go @@ -0,0 +1,14 @@ +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 +}