forked from mirror/pkger
203 lines
3.7 KiB
Go
203 lines
3.7 KiB
Go
package pkger
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/gobuffalo/here"
|
|
)
|
|
|
|
const timeFmt = time.RFC3339Nano
|
|
|
|
type File struct {
|
|
info *FileInfo
|
|
her here.Info
|
|
path Path
|
|
data []byte
|
|
index *index
|
|
Source io.ReadCloser
|
|
}
|
|
|
|
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
|
|
m["data"] = f.data
|
|
if len(f.data) == 0 {
|
|
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
|
|
}
|
|
|
|
ind, ok := m["index"]
|
|
if !ok {
|
|
return fmt.Errorf("missing index")
|
|
}
|
|
f.index = &index{
|
|
Files: map[Path]*File{},
|
|
}
|
|
if err := json.Unmarshal(ind, f.index); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *File) Open(name string) (http.File, error) {
|
|
if f.index == nil {
|
|
f.index = &index{
|
|
Files: map[Path]*File{},
|
|
}
|
|
}
|
|
pt, err := Parse(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(pt.Pkg) == 0 {
|
|
pt.Pkg = f.path.Pkg
|
|
}
|
|
|
|
h := httpFile{
|
|
crs: &byteCRS{bytes.NewReader(f.data)},
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
bf, err := f.her.Open(h.File.Path())
|
|
if err != nil {
|
|
return h, err
|
|
}
|
|
|
|
fi, err := bf.Stat()
|
|
if err != nil {
|
|
return h, err
|
|
}
|
|
|
|
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) Close() error {
|
|
if f.Source == nil {
|
|
return nil
|
|
}
|
|
if c, ok := f.Source.(io.Closer); ok {
|
|
return c.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f File) Name() string {
|
|
return f.info.Name()
|
|
}
|
|
|
|
func (f File) Path() string {
|
|
return f.her.FilePath(f.Name())
|
|
}
|
|
|
|
func (f File) String() string {
|
|
if f.info == nil {
|
|
return ""
|
|
}
|
|
b, _ := json.MarshalIndent(f.info, "", " ")
|
|
return string(b)
|
|
}
|
|
|
|
func (f *File) Read(p []byte) (int, error) {
|
|
if f.Source != nil {
|
|
return f.Source.Read(p)
|
|
}
|
|
|
|
of, err := f.her.Open(f.Path())
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
f.Source = of
|
|
return f.Source.Read(p)
|
|
}
|
|
|
|
// 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) {
|
|
of, err := f.her.Open(f.Path())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer of.Close()
|
|
return of.Readdir(count)
|
|
}
|