github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/filesystem/normalize.go (about)

     1  package filesystem
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/user"
     7  	"path/filepath"
     8  )
     9  
    10  // tildeExpand attempts tilde expansion of paths beginning with ~/ or
    11  // ~<username>/. On Windows, it additionally supports ~\ and ~<username>\.
    12  func tildeExpand(path string) (string, error) {
    13  	// Only process relevant paths.
    14  	if path == "" || path[0] != '~' {
    15  		return path, nil
    16  	}
    17  
    18  	// Find the first character in the path that's considered a path separator
    19  	// on the platform. Path seperators are always single-byte, so we can safely
    20  	// loop over the path's bytes.
    21  	pathSeparatorIndex := -1
    22  	for i := 0; i < len(path); i++ {
    23  		if os.IsPathSeparator(path[i]) {
    24  			pathSeparatorIndex = i
    25  			break
    26  		}
    27  	}
    28  
    29  	// Divide the path into the "username" portion and the "subpath" portion -
    30  	// i.e. those portions coming before and after the separator, respectively.
    31  	var username string
    32  	var remaining string
    33  	if pathSeparatorIndex > 0 {
    34  		username = path[1:pathSeparatorIndex]
    35  		remaining = path[pathSeparatorIndex+1:]
    36  	} else {
    37  		username = path[1:]
    38  	}
    39  
    40  	// Compute the relevant home directory. If the username is empty, then we
    41  	// use the current user's home directory, otherwise we need to do a lookup.
    42  	var homeDirectory string
    43  	if username == "" {
    44  		if h, err := os.UserHomeDir(); err != nil {
    45  			return "", fmt.Errorf("unable to compute path to home directory: %w", err)
    46  		} else {
    47  			homeDirectory = h
    48  		}
    49  	} else {
    50  		if u, err := user.Lookup(username); err != nil {
    51  			return "", fmt.Errorf("unable to lookup user: %w", err)
    52  		} else {
    53  			homeDirectory = u.HomeDir
    54  		}
    55  	}
    56  
    57  	// Compute the full path.
    58  	return filepath.Join(homeDirectory, remaining), nil
    59  }
    60  
    61  // Normalize normalizes a path, expanding home directory tildes, converting it
    62  // to an absolute path, and cleaning the result.
    63  func Normalize(path string) (string, error) {
    64  	// Expand any leading tilde.
    65  	path, err := tildeExpand(path)
    66  	if err != nil {
    67  		return "", fmt.Errorf("unable to perform tilde expansion: %w", err)
    68  	}
    69  
    70  	// Convert to an absolute path. This will also invoke filepath.Clean.
    71  	path, err = filepath.Abs(path)
    72  	if err != nil {
    73  		return "", fmt.Errorf("unable to compute absolute path: %w", err)
    74  	}
    75  
    76  	// Success.
    77  	return path, nil
    78  }