github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/fs/fspath/path.go (about)

     1  // Package fspath contains routines for fspath manipulation
     2  package fspath
     3  
     4  import (
     5  	"path"
     6  	"path/filepath"
     7  	"regexp"
     8  	"strings"
     9  
    10  	"github.com/ncw/rclone/fs/driveletter"
    11  )
    12  
    13  // Matcher is a pattern to match an rclone URL
    14  var Matcher = regexp.MustCompile(`^(:?[\w_ -]+):(.*)$`)
    15  
    16  // Parse deconstructs a remote path into configName and fsPath
    17  //
    18  // If the path is a local path then configName will be returned as "".
    19  //
    20  // So "remote:path/to/dir" will return "remote", "path/to/dir"
    21  // and "/path/to/local" will return ("", "/path/to/local")
    22  //
    23  // Note that this will turn \ into / in the fsPath on Windows
    24  func Parse(path string) (configName, fsPath string) {
    25  	parts := Matcher.FindStringSubmatch(path)
    26  	configName, fsPath = "", path
    27  	if parts != nil && !driveletter.IsDriveLetter(parts[1]) {
    28  		configName, fsPath = parts[1], parts[2]
    29  	}
    30  	// change native directory separators to / if there are any
    31  	fsPath = filepath.ToSlash(fsPath)
    32  	return configName, fsPath
    33  }
    34  
    35  // Split splits a remote into a parent and a leaf
    36  //
    37  // if it returns leaf as an empty string then remote is a directory
    38  //
    39  // if it returns parent as an empty string then that means the current directory
    40  //
    41  // The returned values have the property that parent + leaf == remote
    42  // (except under Windows where \ will be translated into /)
    43  func Split(remote string) (parent string, leaf string) {
    44  	remoteName, remotePath := Parse(remote)
    45  	if remoteName != "" {
    46  		remoteName += ":"
    47  	}
    48  	// Construct new remote name without last segment
    49  	parent, leaf = path.Split(remotePath)
    50  	return remoteName + parent, leaf
    51  }
    52  
    53  // JoinRootPath joins any number of path elements into a single path, adding a
    54  // separating slash if necessary. The result is Cleaned; in particular,
    55  // all empty strings are ignored.
    56  // If the first non empty element has a leading "//" this is preserved.
    57  func JoinRootPath(elem ...string) string {
    58  	for i, e := range elem {
    59  		if e != "" {
    60  			if strings.HasPrefix(e, "//") {
    61  				return "/" + path.Clean(strings.Join(elem[i:], "/"))
    62  			}
    63  			return path.Clean(strings.Join(elem[i:], "/"))
    64  		}
    65  	}
    66  	return ""
    67  }