pkger/file.go

250 lines
4.6 KiB
Go
Raw Normal View History

2019-07-31 00:21:26 +03:00
package pkger
import (
"bytes"
"encoding/json"
2019-07-31 18:53:36 +03:00
"fmt"
2019-07-31 00:21:26 +03:00
"io"
2019-07-31 20:58:20 +03:00
"io/ioutil"
2019-07-31 00:21:26 +03:00
"net/http"
"os"
2019-08-01 21:37:01 +03:00
"path"
2019-07-31 00:21:26 +03:00
"time"
"github.com/gobuffalo/here"
2019-08-01 19:03:12 +03:00
"github.com/markbates/pkger/paths"
2019-07-31 00:21:26 +03:00
)
const timeFmt = time.RFC3339Nano
type File struct {
info *FileInfo
her here.Info
2019-08-01 19:03:12 +03:00
path paths.Path
2019-07-31 00:21:26 +03:00
data []byte
index *index
2019-07-31 23:29:49 +03:00
writer io.ReadWriter
2019-07-31 00:21:26 +03:00
Source io.ReadCloser
}
2019-07-31 23:29:49 +03:00
func (f *File) Close() error {
defer func() {
f.Source = nil
f.writer = nil
}()
if f.Source != nil {
if c, ok := f.Source.(io.Closer); ok {
if err := c.Close(); err != nil {
return err
}
}
}
if f.writer == nil {
return nil
}
b, err := ioutil.ReadAll(f.writer)
if err != nil {
return err
}
f.data = b
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 && len(f.data) <= len(p) {
return copy(p, f.data), io.EOF
}
if len(f.data) > 0 {
f.Source = ioutil.NopCloser(bytes.NewReader(f.data))
}
if f.Source != nil {
return f.Source.Read(p)
}
2019-08-01 19:03:12 +03:00
of, err := f.her.Open(f.FilePath())
2019-07-31 23:29:49 +03:00
if err != nil {
return 0, err
}
f.Source = of
return f.Source.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) HereInfo() here.Info {
return f.her
}
2019-07-31 18:53:36 +03:00
func (f File) MarshalJSON() ([]byte, error) {
m := map[string]interface{}{}
m["info"] = f.info
m["her"] = f.her
m["path"] = f.path
m["index"] = f.index
2019-07-31 20:58:20 +03:00
m["data"] = f.data
if len(f.data) == 0 {
b, err := ioutil.ReadAll(&f)
if err != nil {
return nil, err
}
m["data"] = b
}
2019-07-31 18:53:36 +03:00
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
}
ind, ok := m["index"]
if !ok {
return fmt.Errorf("missing index")
}
2019-07-31 23:29:49 +03:00
f.index = newIndex()
2019-07-31 18:53:36 +03:00
if err := json.Unmarshal(ind, f.index); err != nil {
return err
}
return nil
}
2019-07-31 00:21:26 +03:00
func (f *File) Open(name string) (http.File, error) {
if f.index == nil {
2019-07-31 23:29:49 +03:00
f.index = newIndex()
2019-07-31 00:21:26 +03:00
}
2019-08-01 21:37:01 +03:00
// name = strings.TrimPrefix(name, "/")
2019-08-01 19:03:12 +03:00
pt, err := paths.Parse(name)
2019-07-31 00:21:26 +03:00
if err != nil {
return nil, err
}
2019-08-01 21:37:01 +03:00
pt.Name = path.Join(f.Path().Name, pt.Name)
2019-07-31 00:21:26 +03:00
if len(pt.Pkg) == 0 {
pt.Pkg = f.path.Pkg
}
2019-07-31 18:53:36 +03:00
h := httpFile{
crs: &byteCRS{bytes.NewReader(f.data)},
}
2019-07-31 00:21:26 +03:00
if pt == f.path {
h.File = f
} else {
of, err := f.index.Open(pt)
if err != nil {
return nil, err
}
defer of.Close()
h.File = of
}
if len(f.data) > 0 {
return h, nil
}
2019-08-01 19:03:12 +03:00
bf, err := f.her.Open(h.File.FilePath())
2019-07-31 00:21:26 +03:00
if err != nil {
return h, err
}
2019-07-31 18:53:36 +03:00
2019-07-31 00:21:26 +03:00
fi, err := bf.Stat()
if err != nil {
return h, err
}
2019-07-31 18:53:36 +03:00
2019-07-31 00:21:26 +03:00
if fi.IsDir() {
return h, nil
}
if err != nil {
return nil, err
}
h.crs = bf
return h, nil
}
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()
}
2019-08-01 19:03:12 +03:00
func (f File) FilePath() string {
2019-07-31 18:53:36 +03:00
return f.her.FilePath(f.Name())
2019-07-31 00:21:26 +03:00
}
2019-08-01 19:03:12 +03:00
func (f File) Path() paths.Path {
return f.path
}
2019-07-31 00:21:26 +03:00
func (f File) String() string {
if f.info == nil {
return ""
}
b, _ := json.MarshalIndent(f.info, "", " ")
return string(b)
}
// 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.
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
2019-08-01 19:03:12 +03:00
of, err := f.her.Open(f.FilePath())
2019-07-31 00:21:26 +03:00
if err != nil {
return nil, err
}
defer of.Close()
return of.Readdir(count)
}