github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/utils/filesys/localfs.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package filesys
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"io"
    21  	"io/ioutil"
    22  	"os"
    23  	"path/filepath"
    24  	"time"
    25  )
    26  
    27  // LocalFS is the machines local filesystem
    28  var LocalFS = &localFS{}
    29  
    30  type localFS struct {
    31  	cwd string
    32  }
    33  
    34  // LocalFilesysWithWorkingDir returns a new Filesys implementation backed by the local filesystem with the supplied
    35  // working directory.  Path relative operations occur relative to this directory.
    36  func LocalFilesysWithWorkingDir(cwd string) (Filesys, error) {
    37  	absCWD, err := filepath.Abs(cwd)
    38  
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	stat, err := os.Stat(absCWD)
    44  
    45  	if err != nil {
    46  		return nil, err
    47  	} else if !stat.IsDir() {
    48  		return nil, fmt.Errorf("'%s' is not a valid directory", absCWD)
    49  	}
    50  
    51  	return &localFS{absCWD}, nil
    52  }
    53  
    54  // Exists will tell you if a file or directory with a given path already exists, and if it does is it a directory
    55  func (fs *localFS) Exists(path string) (exists bool, isDir bool) {
    56  	var err error
    57  	path, err = fs.Abs(path)
    58  
    59  	if err != nil {
    60  		return false, false
    61  	}
    62  
    63  	stat, err := os.Stat(path)
    64  
    65  	if err != nil {
    66  		return false, false
    67  	}
    68  
    69  	return true, stat.IsDir()
    70  }
    71  
    72  var errStopMarker = errors.New("stop")
    73  
    74  // Iter iterates over the files and subdirectories within a given directory (Optionally recursively.
    75  func (fs *localFS) Iter(path string, recursive bool, cb FSIterCB) error {
    76  	var err error
    77  	path, err = fs.Abs(path)
    78  
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	if !recursive {
    84  		info, err := ioutil.ReadDir(path)
    85  
    86  		if err != nil {
    87  			return err
    88  		}
    89  
    90  		for _, curr := range info {
    91  			stop := cb(filepath.Join(path, curr.Name()), curr.Size(), curr.IsDir())
    92  
    93  			if stop {
    94  				return nil
    95  			}
    96  		}
    97  
    98  		return nil
    99  	}
   100  
   101  	return fs.iter(path, cb)
   102  }
   103  
   104  func (fs *localFS) iter(dir string, cb FSIterCB) error {
   105  	var err error
   106  	dir, err = fs.Abs(dir)
   107  
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
   113  		if dir != path {
   114  			stop := cb(path, info.Size(), info.IsDir())
   115  
   116  			if stop {
   117  				return errStopMarker
   118  			}
   119  		}
   120  		return nil
   121  	})
   122  
   123  	if err == errStopMarker {
   124  		return nil
   125  	}
   126  
   127  	return err
   128  }
   129  
   130  // OpenForRead opens a file for reading
   131  func (fs *localFS) OpenForRead(fp string) (io.ReadCloser, error) {
   132  	var err error
   133  	fp, err = fs.Abs(fp)
   134  
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	if exists, isDir := fs.Exists(fp); !exists {
   140  		return nil, os.ErrNotExist
   141  	} else if isDir {
   142  		return nil, ErrIsDir
   143  	}
   144  
   145  	return os.Open(fp)
   146  }
   147  
   148  // ReadFile reads the entire contents of a file
   149  func (fs *localFS) ReadFile(fp string) ([]byte, error) {
   150  	var err error
   151  	fp, err = fs.Abs(fp)
   152  
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	return ioutil.ReadFile(fp)
   158  }
   159  
   160  // OpenForWrite opens a file for writing.  The file will be created if it does not exist, and if it does exist
   161  // it will be overwritten.
   162  func (fs *localFS) OpenForWrite(fp string, perm os.FileMode) (io.WriteCloser, error) {
   163  	var err error
   164  	fp, err = fs.Abs(fp)
   165  
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	return os.OpenFile(fp, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, perm)
   171  }
   172  
   173  // WriteFile writes the entire data buffer to a given file.  The file will be created if it does not exist,
   174  // and if it does exist it will be overwritten.
   175  func (fs *localFS) WriteFile(fp string, data []byte) error {
   176  	var err error
   177  	fp, err = fs.Abs(fp)
   178  
   179  	if err != nil {
   180  		return err
   181  	}
   182  
   183  	return ioutil.WriteFile(fp, data, os.ModePerm)
   184  }
   185  
   186  // MkDirs creates a folder and all the parent folders that are necessary to create it.
   187  func (fs *localFS) MkDirs(path string) error {
   188  	var err error
   189  	path, err = fs.Abs(path)
   190  
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	_, err = os.Stat(path)
   196  
   197  	if err != nil {
   198  		return os.MkdirAll(path, os.ModePerm)
   199  	}
   200  
   201  	return nil
   202  }
   203  
   204  // DeleteFile will delete a file at the given path
   205  func (fs *localFS) DeleteFile(path string) error {
   206  	var err error
   207  	path, err = fs.Abs(path)
   208  
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	if exists, isDir := fs.Exists(path); exists && !isDir {
   214  		if isDir {
   215  			return ErrIsDir
   216  		}
   217  
   218  		return os.Remove(path)
   219  	}
   220  
   221  	return os.ErrNotExist
   222  }
   223  
   224  // Delete will delete an empty directory, or a file.  If trying delete a directory that is not empty you can set force to
   225  // true in order to delete the dir and all of it's contents
   226  func (fs *localFS) Delete(path string, force bool) error {
   227  	var err error
   228  	path, err = fs.Abs(path)
   229  
   230  	if err != nil {
   231  		return err
   232  	}
   233  
   234  	if !force {
   235  		return os.Remove(path)
   236  	} else {
   237  		return os.RemoveAll(path)
   238  	}
   239  }
   240  
   241  // MoveFile will move a file from the srcPath in the filesystem to the destPath
   242  func (fs *localFS) MoveFile(srcPath, destPath string) error {
   243  	var err error
   244  	srcPath, err = fs.Abs(srcPath)
   245  
   246  	if err != nil {
   247  		return err
   248  	}
   249  
   250  	destPath, err = fs.Abs(destPath)
   251  
   252  	if err != nil {
   253  		return err
   254  	}
   255  
   256  	return os.Rename(srcPath, destPath)
   257  }
   258  
   259  // converts a path to an absolute path.  If it's already an absolute path the input path will be returned unaltered
   260  func (fs *localFS) Abs(path string) (string, error) {
   261  	if filepath.IsAbs(path) {
   262  		return path, nil
   263  	}
   264  
   265  	if fs.cwd == "" {
   266  		return filepath.Abs(path)
   267  	} else {
   268  		return filepath.Join(fs.cwd, path), nil
   269  	}
   270  }
   271  
   272  // LastModified gets the last modified timestamp for a file or directory at a given path
   273  func (fs *localFS) LastModified(path string) (t time.Time, exists bool) {
   274  	var err error
   275  	path, err = fs.Abs(path)
   276  
   277  	if err != nil {
   278  		return time.Time{}, false
   279  	}
   280  
   281  	stat, err := os.Stat(path)
   282  
   283  	if err != nil {
   284  		return time.Time{}, false
   285  	}
   286  
   287  	return stat.ModTime(), true
   288  }