pathutil.go raw

   1  package pathutil
   2  
   3  import (
   4  	"fmt"
   5  	"os"
   6  	"path/filepath"
   7  )
   8  
   9  // Unique eliminates the duplicate paths from the provided slice and returns
  10  // the result. The paths are expanded using the `ExpandHome` function and only
  11  // absolute paths are kept. The items in the output slice are in the order in
  12  // which they occur in the input slice.
  13  func Unique(paths []string) []string {
  14  	var (
  15  		uniq     []string
  16  		registry = map[string]struct{}{}
  17  	)
  18  
  19  	for _, p := range paths {
  20  		if p = ExpandHome(p); p != "" && filepath.IsAbs(p) {
  21  			if _, ok := registry[p]; ok {
  22  				continue
  23  			}
  24  
  25  			registry[p] = struct{}{}
  26  			uniq = append(uniq, p)
  27  		}
  28  	}
  29  
  30  	return uniq
  31  }
  32  
  33  // First returns the first absolute path from the provided slice.
  34  // The paths in the input slice are expanded using the `ExpandHome` function.
  35  func First(paths []string) string {
  36  	for _, p := range paths {
  37  		if p = ExpandHome(p); p != "" && filepath.IsAbs(p) {
  38  			return p
  39  		}
  40  	}
  41  
  42  	return ""
  43  }
  44  
  45  // Create returns a suitable location relative to which the file with the
  46  // specified `name` can be written. The first path from the provided `paths`
  47  // slice which is successfully created (or already exists) is used as a base
  48  // path for the file. The `name` parameter should contain the name of the file
  49  // which is going to be written in the location returned by this function, but
  50  // it can also contain a set of parent directories, which will be created
  51  // relative to the selected parent path.
  52  func Create(name string, paths []string) (string, error) {
  53  	searchedPaths := make([]string, 0, len(paths))
  54  	for _, p := range paths {
  55  		p = filepath.Join(p, name)
  56  
  57  		dir := filepath.Dir(p)
  58  		if Exists(dir) {
  59  			return p, nil
  60  		}
  61  		if err := os.MkdirAll(dir, os.ModeDir|0o700); err == nil {
  62  			return p, nil
  63  		}
  64  
  65  		searchedPaths = append(searchedPaths, dir)
  66  	}
  67  
  68  	return "", fmt.Errorf("could not create any of the following paths: %v",
  69  		searchedPaths)
  70  }
  71  
  72  // Search searches for the file with the specified `name` in the provided
  73  // slice of `paths`. The `name` parameter must contain the name of the file,
  74  // but it can also contain a set of parent directories.
  75  func Search(name string, paths []string) (string, error) {
  76  	searchedPaths := make([]string, 0, len(paths))
  77  	for _, p := range paths {
  78  		p = filepath.Join(p, name)
  79  		if Exists(p) {
  80  			return p, nil
  81  		}
  82  
  83  		searchedPaths = append(searchedPaths, filepath.Dir(p))
  84  	}
  85  
  86  	return "", fmt.Errorf("could not locate `%s` in any of the following paths: %v",
  87  		filepath.Base(name), searchedPaths)
  88  }
  89  
  90  // EnvPath returns the value of the environment variable with the specified
  91  // `name` if it is an absolute path, or the first absolute fallback path.
  92  // All paths are expanded using the `ExpandHome` function.
  93  func EnvPath(name string, fallbackPaths ...string) string {
  94  	dir := ExpandHome(os.Getenv(name))
  95  	if dir != "" && filepath.IsAbs(dir) {
  96  		return dir
  97  	}
  98  
  99  	return First(fallbackPaths)
 100  }
 101  
 102  // EnvPathList reads the value of the environment variable with the specified
 103  // `name` and attempts to extract a list of absolute paths from it. If there
 104  // are none, a list of absolute fallback paths is returned instead. Duplicate
 105  // paths are removed from the returned slice. All paths are expanded using the
 106  // `ExpandHome` function.
 107  func EnvPathList(name string, fallbackPaths ...string) []string {
 108  	dirs := Unique(filepath.SplitList(os.Getenv(name)))
 109  	if len(dirs) != 0 {
 110  		return dirs
 111  	}
 112  
 113  	return Unique(fallbackPaths)
 114  }
 115