github.com/3JoB/vfs@v1.0.0/filesystem.go (about)

     1  package vfs
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"os"
     7  	"strings"
     8  )
     9  
    10  var (
    11  	// ErrIsDirectory is returned if a file is a directory
    12  	ErrIsDirectory = errors.New("is directory")
    13  
    14  	// ErrNotDirectory is returned if a file is not a directory
    15  	ErrNotDirectory = errors.New("is not a directory")
    16  )
    17  
    18  // Filesystem represents an abstract filesystem
    19  type Filesystem interface {
    20  	PathSeparator() uint8
    21  	OpenFile(name string, flag int, perm os.FileMode) (File, error)
    22  	Remove(name string) error
    23  
    24  	// RemoveAll(path string) error
    25  	Rename(oldpath, newpath string) error
    26  
    27  	Mkdir(name string, perm os.FileMode) error
    28  
    29  	Symlink(oldname, newname string) error
    30  
    31  	// TempDir() string
    32  	// Chmod(name string, mode FileMode) error
    33  	// Chown(name string, uid, gid int) error
    34  	Stat(name string) (os.FileInfo, error)
    35  
    36  	Lstat(name string) (os.FileInfo, error)
    37  	ReadDir(path string) ([]os.FileInfo, error)
    38  }
    39  
    40  // File represents a File with common operations.
    41  // It differs from os.File so e.g. Stat() needs to be called from the Filesystem instead.
    42  //
    43  //	osfile.Stat() -> filesystem.Stat(file.Name())
    44  type File interface {
    45  	Name() string
    46  	Sync() error
    47  
    48  	// Truncate shrinks or extends the size of the File to the specified size.
    49  	Truncate(int64) error
    50  
    51  	io.Reader
    52  	io.ReaderAt
    53  	io.Writer
    54  	io.Seeker
    55  	io.Closer
    56  }
    57  
    58  // Create creates the named file mode 0666 (before umask) on the given Filesystem,
    59  // truncating it if it already exists.
    60  // The associated file descriptor has mode os.O_RDWR.
    61  // If there is an error, it will be of type *os.PathError.
    62  func Create(fs Filesystem, name string) (File, error) {
    63  	return fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
    64  }
    65  
    66  // Open opens the named file on the given Filesystem for reading.
    67  // If successful, methods on the returned file can be used for reading.
    68  // The associated file descriptor has mode os.O_RDONLY.
    69  // If there is an error, it will be of type *PathError.
    70  func Open(fs Filesystem, name string) (File, error) {
    71  	return fs.OpenFile(name, os.O_RDONLY, 0)
    72  }
    73  
    74  // MkdirAll creates a directory named path on the given Filesystem,
    75  // along with any necessary parents, and returns nil,
    76  // or else returns an error.
    77  // The permission bits perm are used for all
    78  // directories that MkdirAll creates.
    79  // If path is already a directory, MkdirAll does nothing
    80  // and returns nil.
    81  func MkdirAll(fs Filesystem, path string, perm os.FileMode) error {
    82  	if dir, err := fs.Stat(path); err == nil {
    83  		if dir.IsDir() {
    84  			return nil
    85  		}
    86  		return &os.PathError{Op: "mkdir", Path: path, Err: ErrNotDirectory}
    87  	}
    88  
    89  	parts := SplitPath(path, string(fs.PathSeparator()))
    90  	if len(parts) > 1 {
    91  		// Create parent
    92  		err := MkdirAll(fs, strings.Join(parts[0:len(parts)-1], string(fs.PathSeparator())), perm)
    93  		if err != nil {
    94  			return err
    95  		}
    96  	}
    97  
    98  	// Parent now exists; invoke Mkdir and use its result.
    99  	err := fs.Mkdir(path, perm)
   100  	if err != nil {
   101  		// Handle arguments like "foo/." by
   102  		// double-checking that directory doesn't exist.
   103  		dir, err1 := fs.Lstat(path)
   104  		if err1 == nil && dir.IsDir() {
   105  			return nil
   106  		}
   107  		return err
   108  	}
   109  	return nil
   110  }
   111  
   112  // RemoveAll removes path and any children it contains.
   113  // It removes everything it can but returns the first error
   114  // it encounters.  If the path does not exist, RemoveAll
   115  // returns nil.
   116  func RemoveAll(fs Filesystem, path string) error {
   117  	if err := fs.Remove(path); err == nil || os.IsNotExist(err) {
   118  		return nil
   119  	}
   120  
   121  	// We could not delete it, so might be a directory
   122  	fis, err := fs.ReadDir(path)
   123  	if err != nil {
   124  		if os.IsNotExist(err) {
   125  			return nil
   126  		}
   127  		return err
   128  	}
   129  
   130  	// Remove contents & return first error.
   131  	err = nil
   132  	for _, fi := range fis {
   133  		err1 := RemoveAll(fs, path+string(fs.PathSeparator())+fi.Name())
   134  		if err == nil {
   135  			err = err1
   136  		}
   137  	}
   138  
   139  	// Remove directory itself.
   140  	err1 := fs.Remove(path)
   141  	if err1 == nil || os.IsNotExist(err1) {
   142  		return nil
   143  	}
   144  	if err == nil {
   145  		err = err1
   146  	}
   147  	return err
   148  }