github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/webutil/datasource.go (about) 1 package webutil 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 "time" 10 ) 11 12 // DataSource 13 type DataSource interface { 14 // OpenData opens a readable stream of data for the file. 15 OpenData() (ReadSeekCloser, error) 16 // Stat provides metadata about the underlying resource, particularly ModTime() 17 Stat() (os.FileInfo, error) 18 } 19 20 // ReadSeekCloser is exactly what you think it is - it combines io.Reader, io.Seeker and io.Closer 21 type ReadSeekCloser interface { 22 io.Closer 23 io.Reader 24 io.Seeker 25 } 26 27 // type FileDataSource struct { 28 // fs afero.Fs // hm, this is a problem... webutil really shouldn't require afero; maybe this is where we internalize the Fs interface... 29 // p string 30 // } 31 32 // func (fds *FileDataSource) OpenData() (ReadSeekCloser, error) { 33 // f, err := fds.fs.Open(fds.p) 34 // if err != nil { 35 // return nil, err 36 // } 37 // return f, nil 38 // } 39 40 // func NewFileDataSource(fs afero.Fs, path string) DataSource { 41 // return &FileDataSource{fs: fs, p: path} 42 // } 43 44 type BytesDataSource struct { 45 FileInfo os.FileInfo 46 b []byte 47 } 48 49 func (bds *BytesDataSource) String() string { 50 var s string 51 if len(bds.b) > 64 { 52 s = string(bds.b[:64]) + "..." 53 } else { 54 s = string(bds.b) 55 } 56 // TODO: add FileInfo.Name()? 57 return fmt.Sprintf("&BytesDataSource{b=%q}", s) 58 } 59 60 func (bds *BytesDataSource) Stat() (os.FileInfo, error) { 61 return bds.FileInfo, nil 62 // return &fileInfo{ 63 // name: bds.name, 64 // size: len(bds.b), 65 // mode: os.FileMode(0644), 66 // modTime: bds.modTime, 67 // }, nil 68 } 69 70 type byteData struct { 71 *bytes.Reader 72 } 73 74 func (d *byteData) Close() error { return nil } 75 76 func (fds *BytesDataSource) OpenData() (ReadSeekCloser, error) { 77 return &byteData{Reader: bytes.NewReader(fds.b)}, nil 78 } 79 80 func NewBytesDataSource(b []byte, name string, modTime time.Time) *BytesDataSource { 81 return &BytesDataSource{ 82 b: b, 83 FileInfo: &fileInfo{ 84 name: name, 85 size: int64(len(b)), 86 mode: os.FileMode(0644), 87 modTime: modTime, 88 }, 89 } 90 } 91 92 type HTTPFSDataSource struct { 93 fs http.FileSystem 94 p string 95 } 96 97 func (fds *HTTPFSDataSource) OpenData() (ReadSeekCloser, error) { 98 f, err := fds.fs.Open(fds.p) 99 if err != nil { 100 return nil, err 101 } 102 return f, nil 103 } 104 105 func (fds *HTTPFSDataSource) Stat() (os.FileInfo, error) { 106 f, err := fds.fs.Open(fds.p) 107 if err != nil { 108 return nil, err 109 } 110 defer f.Close() 111 return f.Stat() 112 } 113 114 func NewHTTPFSDataSource(fs http.FileSystem, path string) *HTTPFSDataSource { 115 return &HTTPFSDataSource{fs: fs, p: path} 116 } 117 118 type fileInfo struct { 119 name string 120 size int64 121 mode os.FileMode 122 modTime time.Time 123 isDir bool 124 sys interface{} 125 } 126 127 func (fi *fileInfo) Name() string { return fi.name } 128 func (fi *fileInfo) Size() int64 { return fi.size } 129 func (fi *fileInfo) Mode() os.FileMode { return fi.mode } 130 func (fi *fileInfo) ModTime() time.Time { return fi.modTime } 131 func (fi *fileInfo) IsDir() bool { return fi.isDir } 132 func (fi *fileInfo) Sys() interface{} { return fi.sys }