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-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
}