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  }