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 }