forked from lug/matterbridge
		
	
		
			
				
	
	
		
			253 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			253 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package rice
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"sort"
 | |
| 
 | |
| 	"github.com/GeertJohan/go.rice/embedded"
 | |
| )
 | |
| 
 | |
| //++ TODO: IDEA: merge virtualFile and virtualDir, this decreases work done by rice.File
 | |
| 
 | |
| // Error indicating some function is not implemented yet (but available to satisfy an interface)
 | |
| var ErrNotImplemented = errors.New("not implemented yet")
 | |
| 
 | |
| // virtualFile is a 'stateful' virtual file.
 | |
| // virtualFile wraps an *EmbeddedFile for a call to Box.Open() and virtualizes 'read cursor' (offset) and 'closing'.
 | |
| // virtualFile is only internally visible and should be exposed through rice.File
 | |
| type virtualFile struct {
 | |
| 	*embedded.EmbeddedFile       // the actual embedded file, embedded to obtain methods
 | |
| 	offset                 int64 // read position on the virtual file
 | |
| 	closed                 bool  // closed when true
 | |
| }
 | |
| 
 | |
| // create a new virtualFile for given EmbeddedFile
 | |
| func newVirtualFile(ef *embedded.EmbeddedFile) *virtualFile {
 | |
| 	vf := &virtualFile{
 | |
| 		EmbeddedFile: ef,
 | |
| 		offset:       0,
 | |
| 		closed:       false,
 | |
| 	}
 | |
| 	return vf
 | |
| }
 | |
| 
 | |
| //++ TODO check for nil pointers in all these methods. When so: return os.PathError with Err: os.ErrInvalid
 | |
| 
 | |
| func (vf *virtualFile) close() error {
 | |
| 	if vf.closed {
 | |
| 		return &os.PathError{
 | |
| 			Op:   "close",
 | |
| 			Path: vf.EmbeddedFile.Filename,
 | |
| 			Err:  errors.New("already closed"),
 | |
| 		}
 | |
| 	}
 | |
| 	vf.EmbeddedFile = nil
 | |
| 	vf.closed = true
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (vf *virtualFile) stat() (os.FileInfo, error) {
 | |
| 	if vf.closed {
 | |
| 		return nil, &os.PathError{
 | |
| 			Op:   "stat",
 | |
| 			Path: vf.EmbeddedFile.Filename,
 | |
| 			Err:  errors.New("bad file descriptor"),
 | |
| 		}
 | |
| 	}
 | |
| 	return (*embeddedFileInfo)(vf.EmbeddedFile), nil
 | |
| }
 | |
| 
 | |
| func (vf *virtualFile) readdir(count int) ([]os.FileInfo, error) {
 | |
| 	if vf.closed {
 | |
| 		return nil, &os.PathError{
 | |
| 			Op:   "readdir",
 | |
| 			Path: vf.EmbeddedFile.Filename,
 | |
| 			Err:  errors.New("bad file descriptor"),
 | |
| 		}
 | |
| 	}
 | |
| 	//TODO: return proper error for a readdir() call on a file
 | |
| 	return nil, ErrNotImplemented
 | |
| }
 | |
| 
 | |
| func (vf *virtualFile) read(bts []byte) (int, error) {
 | |
| 	if vf.closed {
 | |
| 		return 0, &os.PathError{
 | |
| 			Op:   "read",
 | |
| 			Path: vf.EmbeddedFile.Filename,
 | |
| 			Err:  errors.New("bad file descriptor"),
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	end := vf.offset + int64(len(bts))
 | |
| 
 | |
| 	if end >= int64(len(vf.Content)) {
 | |
| 		// end of file, so return what we have + EOF
 | |
| 		n := copy(bts, vf.Content[vf.offset:])
 | |
| 		vf.offset = 0
 | |
| 		return n, io.EOF
 | |
| 	}
 | |
| 
 | |
| 	n := copy(bts, vf.Content[vf.offset:end])
 | |
| 	vf.offset += int64(n)
 | |
| 	return n, nil
 | |
| 
 | |
| }
 | |
| 
 | |
| func (vf *virtualFile) seek(offset int64, whence int) (int64, error) {
 | |
| 	if vf.closed {
 | |
| 		return 0, &os.PathError{
 | |
| 			Op:   "seek",
 | |
| 			Path: vf.EmbeddedFile.Filename,
 | |
| 			Err:  errors.New("bad file descriptor"),
 | |
| 		}
 | |
| 	}
 | |
| 	var e error
 | |
| 
 | |
| 	//++ TODO: check if this is correct implementation for seek
 | |
| 	switch whence {
 | |
| 	case os.SEEK_SET:
 | |
| 		//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
 | |
| 		vf.offset = offset
 | |
| 	case os.SEEK_CUR:
 | |
| 		//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
 | |
| 		vf.offset += offset
 | |
| 	case os.SEEK_END:
 | |
| 		//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
 | |
| 		vf.offset = int64(len(vf.EmbeddedFile.Content)) - offset
 | |
| 	}
 | |
| 
 | |
| 	if e != nil {
 | |
| 		return 0, &os.PathError{
 | |
| 			Op:   "seek",
 | |
| 			Path: vf.Filename,
 | |
| 			Err:  e,
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return vf.offset, nil
 | |
| }
 | |
| 
 | |
| // virtualDir is a 'stateful' virtual directory.
 | |
| // virtualDir wraps an *EmbeddedDir for a call to Box.Open() and virtualizes 'closing'.
 | |
| // virtualDir is only internally visible and should be exposed through rice.File
 | |
| type virtualDir struct {
 | |
| 	*embedded.EmbeddedDir
 | |
| 	offset int // readdir position on the directory
 | |
| 	closed bool
 | |
| }
 | |
| 
 | |
| // create a new virtualDir for given EmbeddedDir
 | |
| func newVirtualDir(ed *embedded.EmbeddedDir) *virtualDir {
 | |
| 	vd := &virtualDir{
 | |
| 		EmbeddedDir: ed,
 | |
| 		offset:      0,
 | |
| 		closed:      false,
 | |
| 	}
 | |
| 	return vd
 | |
| }
 | |
| 
 | |
| func (vd *virtualDir) close() error {
 | |
| 	//++ TODO: needs sync mutex?
 | |
| 	if vd.closed {
 | |
| 		return &os.PathError{
 | |
| 			Op:   "close",
 | |
| 			Path: vd.EmbeddedDir.Filename,
 | |
| 			Err:  errors.New("already closed"),
 | |
| 		}
 | |
| 	}
 | |
| 	vd.closed = true
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (vd *virtualDir) stat() (os.FileInfo, error) {
 | |
| 	if vd.closed {
 | |
| 		return nil, &os.PathError{
 | |
| 			Op:   "stat",
 | |
| 			Path: vd.EmbeddedDir.Filename,
 | |
| 			Err:  errors.New("bad file descriptor"),
 | |
| 		}
 | |
| 	}
 | |
| 	return (*embeddedDirInfo)(vd.EmbeddedDir), nil
 | |
| }
 | |
| 
 | |
| func (vd *virtualDir) readdir(n int) (fi []os.FileInfo, err error) {
 | |
| 
 | |
| 	if vd.closed {
 | |
| 		return nil, &os.PathError{
 | |
| 			Op:   "readdir",
 | |
| 			Path: vd.EmbeddedDir.Filename,
 | |
| 			Err:  errors.New("bad file descriptor"),
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Build up the array of our contents
 | |
| 	var files []os.FileInfo
 | |
| 
 | |
| 	// Add the child directories
 | |
| 	for _, child := range vd.ChildDirs {
 | |
| 		child.Filename = filepath.Base(child.Filename)
 | |
| 		files = append(files, (*embeddedDirInfo)(child))
 | |
| 	}
 | |
| 
 | |
| 	// Add the child files
 | |
| 	for _, child := range vd.ChildFiles {
 | |
| 		child.Filename = filepath.Base(child.Filename)
 | |
| 		files = append(files, (*embeddedFileInfo)(child))
 | |
| 	}
 | |
| 
 | |
| 	// Sort it by filename (lexical order)
 | |
| 	sort.Sort(SortByName(files))
 | |
| 
 | |
| 	// Return all contents if that's what is requested
 | |
| 	if n <= 0 {
 | |
| 		vd.offset = 0
 | |
| 		return files, nil
 | |
| 	}
 | |
| 
 | |
| 	// If user has requested past the end of our list
 | |
| 	// return what we can and send an EOF
 | |
| 	if vd.offset+n >= len(files) {
 | |
| 		offset := vd.offset
 | |
| 		vd.offset = 0
 | |
| 		return files[offset:], io.EOF
 | |
| 	}
 | |
| 
 | |
| 	offset := vd.offset
 | |
| 	vd.offset += n
 | |
| 	return files[offset : offset+n], nil
 | |
| 
 | |
| }
 | |
| 
 | |
| func (vd *virtualDir) read(bts []byte) (int, error) {
 | |
| 	if vd.closed {
 | |
| 		return 0, &os.PathError{
 | |
| 			Op:   "read",
 | |
| 			Path: vd.EmbeddedDir.Filename,
 | |
| 			Err:  errors.New("bad file descriptor"),
 | |
| 		}
 | |
| 	}
 | |
| 	return 0, &os.PathError{
 | |
| 		Op:   "read",
 | |
| 		Path: vd.EmbeddedDir.Filename,
 | |
| 		Err:  errors.New("is a directory"),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (vd *virtualDir) seek(offset int64, whence int) (int64, error) {
 | |
| 	if vd.closed {
 | |
| 		return 0, &os.PathError{
 | |
| 			Op:   "seek",
 | |
| 			Path: vd.EmbeddedDir.Filename,
 | |
| 			Err:  errors.New("bad file descriptor"),
 | |
| 		}
 | |
| 	}
 | |
| 	return 0, &os.PathError{
 | |
| 		Op:   "seek",
 | |
| 		Path: vd.Filename,
 | |
| 		Err:  errors.New("is a directory"),
 | |
| 	}
 | |
| }
 | 
