github.com/goki/ki@v1.1.17/dirs/dirs.go (about) 1 // Copyright (c) 2018, The GoKi Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package dirs provides various utility functions in dealing with directories 6 // such as a list of all the files with a given (set of) extensions and 7 // finding paths within the Go source directory (GOPATH, etc) 8 package dirs 9 10 import ( 11 "errors" 12 "fmt" 13 "go/build" 14 "io/fs" 15 "io/ioutil" 16 "os" 17 "path/filepath" 18 "sort" 19 "strings" 20 "time" 21 ) 22 23 // GoSrcDir tries to locate dir in GOPATH/src/ or GOROOT/src/pkg/ and returns its 24 // full path. GOPATH may contain a list of paths. From Robin Elkind github.com/mewkiz/pkg 25 func GoSrcDir(dir string) (absDir string, err error) { 26 for _, srcDir := range build.Default.SrcDirs() { 27 absDir = filepath.Join(srcDir, dir) 28 finfo, err := os.Stat(absDir) 29 if err == nil && finfo.IsDir() { 30 return absDir, nil 31 } 32 } 33 /* this is probably redundant and not needed -- and UserHomeDir is only in 1.12 34 home, err := os.UserHomeDir() 35 if err != nil { 36 return "", err 37 } 38 absDir = filepath.Join(filepath.Join(filepath.Join(home, "go"), "src"), dir) 39 finfo, err := os.Stat(absDir) 40 if err == nil && finfo.IsDir() { 41 return absDir, nil 42 } 43 */ 44 return "", fmt.Errorf("kit.GoSrcDir: unable to locate directory (%q) in GOPATH/src/ (%q) or GOROOT/src/pkg/ (%q)", dir, os.Getenv("GOPATH"), os.Getenv("GOROOT")) 45 } 46 47 // ExtFiles returns all the FileInfo's for files with given extension(s) in directory 48 // in sorted order (if exts is empty then all files are returned). 49 // In case of error, returns nil. 50 func ExtFiles(path string, exts []string) []os.FileInfo { 51 files, err := ioutil.ReadDir(path) 52 if err != nil { 53 return nil 54 } 55 if len(exts) == 0 { 56 return files 57 } 58 sz := len(files) 59 if sz == 0 { 60 return nil 61 } 62 for i := sz - 1; i >= 0; i-- { 63 fn := files[i] 64 ext := filepath.Ext(fn.Name()) 65 keep := false 66 for _, ex := range exts { 67 if strings.EqualFold(ext, ex) { 68 keep = true 69 break 70 } 71 } 72 if !keep { 73 files = append(files[:i], files[i+1:]...) 74 } 75 } 76 return files 77 } 78 79 // ExtFileNames returns all the file names with given extension(s) in directory 80 // in sorted order (if exts is empty then all files are returned) 81 func ExtFileNames(path string, exts []string) []string { 82 f, err := os.Open(path) 83 if err != nil { 84 return nil 85 } 86 files, err := f.Readdirnames(-1) 87 f.Close() 88 if err != nil { 89 return nil 90 } 91 if len(exts) == 0 { 92 sort.StringSlice(files).Sort() 93 return files 94 } 95 sz := len(files) 96 if sz == 0 { 97 return nil 98 } 99 for i := sz - 1; i >= 0; i-- { 100 fn := files[i] 101 ext := filepath.Ext(fn) 102 keep := false 103 for _, ex := range exts { 104 if strings.EqualFold(ext, ex) { 105 keep = true 106 break 107 } 108 } 109 if !keep { 110 files = append(files[:i], files[i+1:]...) 111 } 112 } 113 sort.StringSlice(files).Sort() 114 return files 115 } 116 117 // Dirs returns a slice of all the directories within a given directory 118 func Dirs(path string) []string { 119 files, err := ioutil.ReadDir(path) 120 if err != nil { 121 return nil 122 } 123 124 var fnms []string 125 for _, fi := range files { 126 if fi.IsDir() { 127 fnms = append(fnms, fi.Name()) 128 } 129 } 130 return fnms 131 } 132 133 // LatestMod returns the latest (most recent) modification time for any of the 134 // files in the directory (optionally filtered by extension(s) if exts != nil) 135 // if no files or error, returns zero time value 136 func LatestMod(path string, exts []string) time.Time { 137 tm := time.Time{} 138 files := ExtFiles(path, exts) 139 if len(files) == 0 { 140 return tm 141 } 142 for _, fi := range files { 143 if fi.ModTime().After(tm) { 144 tm = fi.ModTime() 145 } 146 } 147 return tm 148 } 149 150 // AllFiles returns a slice of all the files, recursively, within a given directory 151 // Due to the nature of the filepath.Walk function, the first entry will be the 152 // directory itself, for reference -- just skip past that if you don't need it. 153 func AllFiles(path string) ([]string, error) { 154 var fnms []string 155 er := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { 156 if err != nil { 157 return err 158 } 159 fnms = append(fnms, path) 160 return nil 161 }) 162 return fnms, er 163 } 164 165 // HasFile returns true if given directory has given file (exact match) 166 func HasFile(path, file string) bool { 167 files, err := ioutil.ReadDir(path) 168 if err != nil { 169 return false 170 } 171 for _, fn := range files { 172 if fn.Name() == file { 173 return true 174 } 175 } 176 return false 177 } 178 179 // note: rejected from std lib, but often need: https://github.com/golang/go/issues/25012 180 // https://github.com/golang/go/issues/5366 181 182 // SplitExt returns the base of the file name without extension, and the extension 183 func SplitExt(fname string) (fbase, ext string) { 184 ext = filepath.Ext(fname) 185 fbase = strings.TrimSuffix(fname, ext) 186 return 187 } 188 189 // FindFileOnPaths attempts to locate given file on given list of paths, 190 // returning the full Abs path to file if found, else error 191 func FindFileOnPaths(paths []string, file string) (string, error) { 192 for _, path := range paths { 193 filePath := filepath.Join(path, file) 194 ok, _ := FileExists(filePath) 195 if ok { 196 return filePath, nil 197 } 198 } 199 return "", fmt.Errorf("FindFileOnPaths: unable to find file: %s on paths: %v\n", file, paths) 200 } 201 202 // FileExists checks whether given file exists, returning true if so, 203 // false if not, and error if there is an error in accessing the file. 204 func FileExists(filePath string) (bool, error) { 205 fileInfo, err := os.Stat(filePath) 206 if err == nil { 207 return !fileInfo.IsDir(), nil 208 } 209 if errors.Is(err, os.ErrNotExist) { 210 return false, nil 211 } 212 return false, err 213 } 214 215 // FileExistsFS checks whether given file exists, returning true if so, 216 // false if not, and error if there is an error in accessing the file. 217 func FileExistsFS(fsys fs.FS, filePath string) (bool, error) { 218 if fsys, ok := fsys.(fs.StatFS); ok { 219 fileInfo, err := fsys.Stat(filePath) 220 if err == nil { 221 return !fileInfo.IsDir(), nil 222 } 223 if errors.Is(err, fs.ErrNotExist) { 224 return false, nil 225 } 226 return false, err 227 } 228 fp, err := fsys.Open(filePath) 229 if err == nil { 230 fp.Close() 231 return true, nil 232 } 233 if errors.Is(err, fs.ErrNotExist) { 234 return false, nil 235 } 236 return false, err 237 }