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-03 23:36:56 +03:00
"path"
2019-07-31 00:21:26 +03:00
"time"
2019-08-05 00:13:27 +03:00
"github.com/markbates/pkger/here"
2019-07-31 00:21:26 +03:00
)
const timeFmt = time . RFC3339Nano
2019-08-02 06:21:37 +03:00
var _ http . File = & File { }
2019-07-31 00:21:26 +03:00
type File struct {
info * FileInfo
her here . Info
2019-08-02 05:34:32 +03:00
path Path
2019-07-31 00:21:26 +03:00
data [ ] byte
2019-08-02 06:21:37 +03:00
writer * bytes . Buffer
2019-08-02 06:23:46 +03:00
reader io . Reader
2019-07-31 00:21:26 +03:00
}
2019-08-02 06:21:37 +03:00
func ( f * File ) Seek ( offset int64 , whence int ) ( int64 , error ) {
2019-08-02 06:23:46 +03:00
if sk , ok := f . reader . ( io . Seeker ) ; ok {
2019-08-02 06:21:37 +03:00
return sk . Seek ( offset , whence )
}
return 0 , nil
}
2019-07-31 23:29:49 +03:00
func ( f * File ) Close ( ) error {
defer func ( ) {
2019-08-02 06:23:46 +03:00
f . reader = nil
2019-07-31 23:29:49 +03:00
f . writer = nil
} ( )
2019-08-02 06:23:46 +03:00
if f . reader != nil {
if c , ok := f . reader . ( io . Closer ) ; ok {
2019-07-31 23:29:49 +03:00
if err := c . Close ( ) ; err != nil {
return err
}
}
}
if f . writer == nil {
return nil
}
2019-08-02 06:21:37 +03:00
f . data = f . writer . Bytes ( )
2019-07-31 23:29:49 +03:00
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 ) {
2019-08-03 23:36:56 +03:00
if len ( f . data ) > 0 && f . reader == nil {
f . reader = bytes . NewReader ( f . data )
2019-07-31 23:29:49 +03:00
}
2019-08-02 06:23:46 +03:00
if f . reader != nil {
return f . reader . Read ( p )
2019-07-31 23:29:49 +03:00
}
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
}
2019-08-02 06:23:46 +03:00
f . reader = of
return f . reader . Read ( p )
2019-07-31 23:29:49 +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-08-02 06:23:46 +03:00
func ( f File ) Info ( ) here . Info {
2019-07-31 23:29:49 +03:00
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
2019-07-31 20:58:20 +03:00
m [ "data" ] = f . data
2019-08-02 07:22:17 +03:00
if len ( f . data ) == 0 && ! f . info . IsDir ( ) {
2019-07-31 20:58:20 +03:00
b , err := ioutil . ReadAll ( & f )
if err != nil {
return nil , err
}
m [ "data" ] = b
}
2019-08-02 22:14:11 +03:00
2019-08-05 00:13:27 +03:00
return json . Marshal ( m )
2019-07-31 18:53:36 +03:00
}
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" )
}
2019-08-02 22:14:11 +03:00
2019-07-31 18:53:36 +03:00
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
}
2019-08-02 22:14:11 +03:00
if err := json . Unmarshal ( m [ "data" ] , & f . data ) ; err != nil {
return err
}
2019-07-31 18:53:36 +03:00
return nil
}
2019-07-31 00:21:26 +03:00
func ( f * File ) Open ( name string ) ( http . File , error ) {
2019-08-02 05:34:32 +03:00
pt , err := Parse ( name )
2019-07-31 00:21:26 +03:00
if err != nil {
return nil , err
}
if pt == f . path {
2019-08-02 06:21:37 +03:00
return f , nil
2019-08-02 05:34:32 +03:00
}
2019-08-03 23:36:56 +03:00
pt . Name = path . Join ( f . Path ( ) . Name , pt . Name )
2019-08-02 06:21:37 +03:00
return rootIndex . Open ( pt )
2019-07-31 00:21:26 +03:00
}
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-02 05:34:32 +03:00
func ( f File ) Path ( ) Path {
2019-08-01 19:03:12 +03:00
return f . path
}
2019-07-31 00:21:26 +03:00
func ( f File ) String ( ) string {
2019-08-02 06:21:37 +03:00
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 ( ) )
2019-07-31 00:21:26 +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.
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 )
}