pkger/pkging/mem/file.go

204 lines
4.8 KiB
Go
Raw Permalink Normal View History

2019-09-03 18:29:28 +03:00
package mem
2019-09-01 00:00:24 +03:00
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"path"
2019-09-03 19:41:21 +03:00
"path/filepath"
2019-09-01 00:00:24 +03:00
"time"
"github.com/markbates/pkger/here"
2019-09-02 01:02:45 +03:00
"github.com/markbates/pkger/pkging"
2019-09-01 00:00:24 +03:00
)
const timeFmt = time.RFC3339Nano
2019-09-02 01:02:45 +03:00
var _ pkging.File = &File{}
2019-09-01 00:00:24 +03:00
type File struct {
2019-10-21 00:52:16 +03:00
Here here.Info
2019-09-02 01:02:45 +03:00
info *pkging.FileInfo
2019-10-09 20:21:54 +03:00
path here.Path
2019-09-01 00:00:24 +03:00
data []byte
2019-10-09 20:21:54 +03:00
parent here.Path
2019-09-01 00:00:24 +03:00
writer *bytes.Buffer
reader io.Reader
2019-09-03 18:29:28 +03:00
pkging pkging.Pkger
2019-09-01 00:00:24 +03:00
}
2019-10-16 23:09:17 +03:00
// Seek sets the offset for the next Read or Write on file to offset, interpreted according to whence: 0 means relative to the origin of the file, 1 means relative to the current offset, and 2 means relative to the end. It returns the new offset and an error, if any.
2019-09-02 01:02:45 +03:00
func (f *File) Seek(ofpkginget int64, whence int) (int64, error) {
2019-11-20 23:36:29 +03:00
if len(f.data) > 0 && f.reader == nil {
f.reader = bytes.NewReader(f.data)
}
2019-09-01 00:00:24 +03:00
if sk, ok := f.reader.(io.Seeker); ok {
2019-09-02 01:02:45 +03:00
return sk.Seek(ofpkginget, whence)
2019-09-01 00:00:24 +03:00
}
return 0, nil
}
2019-10-16 23:09:17 +03:00
// Close closes the File, rendering it unusable for I/O.
2019-09-01 00:00:24 +03:00
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))
2019-09-02 01:02:45 +03:00
fi.Details.ModTime = pkging.ModTime(time.Now())
2019-09-01 00:00:24 +03:00
f.info = fi
return nil
}
2019-10-16 23:09:17 +03:00
// Read reads up to len(b) bytes from the File. It returns the number of bytes read and any error encountered. At end of file, Read returns 0, io.EOF.
2019-09-01 00:00:24 +03:00
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())
}
2019-10-16 23:09:17 +03:00
// Write writes len(b) bytes to the File. It returns the number of bytes written and an error, if any. Write returns a non-nil error when n != len(b).
2019-09-01 00:00:24 +03:00
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
}
2019-10-16 23:09:17 +03:00
// Info returns the here.Info of the file
2019-09-01 00:00:24 +03:00
func (f File) Info() here.Info {
2019-10-21 00:52:16 +03:00
return f.Here
2019-09-01 00:00:24 +03:00
}
2019-10-16 23:09:17 +03:00
// Stat returns the FileInfo structure describing file. If there is an error, it will be of type *PathError.
2019-09-01 00:00:24 +03:00
func (f File) Stat() (os.FileInfo, error) {
if f.info == nil {
return nil, os.ErrNotExist
}
return f.info, nil
}
2019-10-24 23:22:15 +03:00
// Name retuns the name of the file in pkger format
2019-09-01 00:00:24 +03:00
func (f File) Name() string {
2019-10-24 23:22:15 +03:00
return f.path.String()
2019-09-01 00:00:24 +03:00
}
2019-10-16 23:09:17 +03:00
// Path returns the here.Path of the file
2019-10-09 20:21:54 +03:00
func (f File) Path() here.Path {
2019-09-01 00:00:24 +03:00
return f.path
}
func (f File) String() string {
return f.Path().String()
}
2019-10-16 23:09:17 +03:00
// Readdir reads the contents of the directory associated with file and returns a slice of up to n FileInfo values, as would be returned by Lstat, in directory order. Subsequent calls on the same file will yield further FileInfos.
//
// If n > 0, Readdir returns at most n FileInfo structures. In this case, if Readdir returns an empty slice, it will return a non-nil error explaining why. At the end of a directory, the error is io.EOF.
//
// If n <= 0, Readdir returns all the FileInfo from the directory in a single slice. In this case, if Readdir succeeds (reads all the way to the end of the directory), it returns the slice and a nil error. If it encounters an error before the end of the directory, Readdir returns the FileInfo read until that point and a non-nil error.
2019-09-01 00:00:24 +03:00
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
var infos []os.FileInfo
root := f.Path().String()
2019-09-02 01:02:45 +03:00
err := f.pkging.Walk(root, func(path string, info os.FileInfo, err error) error {
2019-09-01 00:00:24 +03:00
if err != nil {
return err
}
if count > 0 && len(infos) == count {
return io.EOF
}
2019-09-21 21:05:59 +03:00
if root == path {
return nil
}
2019-09-02 01:02:45 +03:00
pt, err := f.pkging.Parse(path)
2019-09-01 00:00:24 +03:00
if err != nil {
return err
}
if pt.Name == f.parent.Name {
return nil
}
2019-09-03 19:41:21 +03:00
2019-09-01 00:00:24 +03:00
infos = append(infos, info)
2019-09-03 19:41:21 +03:00
if info.IsDir() && path != root {
return filepath.SkipDir
}
2019-09-01 00:00:24 +03:00
return nil
})
if err != nil {
if _, ok := err.(*os.PathError); ok {
return infos, nil
}
if err != io.EOF {
return nil, err
}
}
return infos, nil
}
2019-10-16 23:09:17 +03:00
// Open implements the http.FileSystem interface. A FileSystem implements access to a collection of named files. The elements in a file path are separated by slash ('/', U+002F) characters, regardless of host operating system convention.
2019-09-01 00:00:24 +03:00
func (f *File) Open(name string) (http.File, error) {
2019-10-21 00:52:16 +03:00
pt, err := f.Here.Parse(name)
2019-09-01 00:00:24 +03:00
if err != nil {
return nil, err
}
if pt == f.path {
return f, nil
}
pt.Name = path.Join(f.Path().Name, pt.Name)
2019-09-02 01:02:45 +03:00
di, err := f.pkging.Open(pt.String())
2019-09-01 00:00:24 +03:00
if err != nil {
return nil, err
}
fi, err := di.Stat()
if err != nil {
return nil, err
}
2019-10-16 00:37:51 +03:00
2019-09-01 00:00:24 +03:00
if fi.IsDir() {
d2 := &File{
2019-09-02 01:02:45 +03:00
info: pkging.NewFileInfo(fi),
2019-10-21 00:52:16 +03:00
Here: di.Info(),
2019-09-01 00:00:24 +03:00
path: pt,
parent: f.path,
2019-09-02 01:02:45 +03:00
pkging: f.pkging,
2019-09-01 00:00:24 +03:00
}
di = d2
}
return di, nil
}