github.com/tilt-dev/wat@v0.0.2-0.20180626175338-9349b638e250/data/pathutil/pathutil.go (about)

     1  // Common utilities for DB paths and OS paths
     2  
     3  package pathutil
     4  
     5  import (
     6  	"strings"
     7  )
     8  
     9  type PathUtil interface {
    10  	Base(path string) string
    11  	Dir(path string) string
    12  	Join(dir, base string) string
    13  	Match(pattern, name string) (matched bool, err error)
    14  	Separator() rune
    15  }
    16  
    17  // Split the path into (firstDir, rest). Note that this is
    18  // different from normal Split(), which splits things into (dir, base).
    19  // SplitFirst is better for matching algorithms.
    20  //
    21  // If the path cannot be split, returns (p, "").
    22  func SplitFirst(util PathUtil, p string) (string, string) {
    23  	firstSlash := strings.IndexRune(p, util.Separator())
    24  	if firstSlash == -1 {
    25  		return p, ""
    26  	}
    27  
    28  	return p[0:firstSlash], p[firstSlash+1:]
    29  }
    30  
    31  // Given absolute paths `dir` and `file`, returns
    32  // the relative path of `file` relative to `dir`.
    33  //
    34  // Returns true if successful. If `file` is not under `dir`, returns false.
    35  func Child(util PathUtil, dir, file string) (string, bool) {
    36  	current := file
    37  	child := ""
    38  	for true {
    39  		if dir == current {
    40  			return child, true
    41  		}
    42  
    43  		if len(current) <= len(dir) || current == "." {
    44  			return "", false
    45  		}
    46  
    47  		cDir := util.Dir(current)
    48  		cBase := util.Base(current)
    49  		child = util.Join(cBase, child)
    50  		current = cDir
    51  	}
    52  
    53  	return "", false
    54  }
    55  
    56  // Like Child(), but file is a pattern instead of a path.
    57  func childPattern(util PathUtil, dir, filePattern string) (string, bool) {
    58  	current := filePattern
    59  	child := ""
    60  	for true {
    61  		matched := (&patternMatcher{util: util, pattern: current}).Match(dir)
    62  		if matched {
    63  			// The recursive glob (**) can match multiple directories.
    64  			// So if current terminates in a recursive glob, then the child pattern
    65  			// must also begin with a recursive glob.
    66  			currentBase := util.Base(current)
    67  			if currentBase == "**" {
    68  				return util.Join("**", child), true
    69  			}
    70  
    71  			return child, true
    72  		}
    73  
    74  		if current == "." || current == "" {
    75  			return "", false
    76  		}
    77  
    78  		cDir := util.Dir(current)
    79  		cBase := util.Base(current)
    80  		child = util.Join(cBase, child)
    81  		current = cDir
    82  	}
    83  
    84  	return "", false
    85  }