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 }