diff options
Diffstat (limited to 'cli/internal/turbopath/absolute_system_path.go')
| -rw-r--r-- | cli/internal/turbopath/absolute_system_path.go | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/cli/internal/turbopath/absolute_system_path.go b/cli/internal/turbopath/absolute_system_path.go new file mode 100644 index 0000000..df65827 --- /dev/null +++ b/cli/internal/turbopath/absolute_system_path.go @@ -0,0 +1,258 @@ +package turbopath + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +// AbsoluteSystemPath is a root-relative path using system separators. +type AbsoluteSystemPath string + +// _dirPermissions are the default permission bits we apply to directories. +const _dirPermissions = os.ModeDir | 0775 + +// _nonRelativeSentinel is the leading sentinel that indicates traversal. +const _nonRelativeSentinel = ".." + +// ToString returns a string represenation of this Path. +// Used for interfacing with APIs that require a string. +func (p AbsoluteSystemPath) ToString() string { + return string(p) +} + +// RelativeTo calculates the relative path between two `AbsoluteSystemPath`s. +func (p AbsoluteSystemPath) RelativeTo(basePath AbsoluteSystemPath) (AnchoredSystemPath, error) { + processed, err := filepath.Rel(basePath.ToString(), p.ToString()) + return AnchoredSystemPath(processed), err +} + +// Join appends relative path segments to this AbsoluteSystemPath. +func (p AbsoluteSystemPath) Join(additional ...RelativeSystemPath) AbsoluteSystemPath { + cast := RelativeSystemPathArray(additional) + return AbsoluteSystemPath(filepath.Join(p.ToString(), filepath.Join(cast.ToStringArray()...))) +} + +// ToStringDuringMigration returns a string representation of this path. +// These instances should eventually be removed. +func (p AbsoluteSystemPath) ToStringDuringMigration() string { + return p.ToString() +} + +// UntypedJoin is a Join that does not constrain the type of the arguments. +// This enables you to pass in strings, but does not protect you from garbage in. +func (p AbsoluteSystemPath) UntypedJoin(args ...string) AbsoluteSystemPath { + return AbsoluteSystemPath(filepath.Join(p.ToString(), filepath.Join(args...))) +} + +// Dir implements filepath.Dir() for an AbsoluteSystemPath +func (p AbsoluteSystemPath) Dir() AbsoluteSystemPath { + return AbsoluteSystemPath(filepath.Dir(p.ToString())) +} + +// Mkdir implements os.Mkdir(p, perm) +func (p AbsoluteSystemPath) Mkdir(perm os.FileMode) error { + return os.Mkdir(p.ToString(), perm) +} + +// MkdirAll implements os.MkdirAll(p, perm) +func (p AbsoluteSystemPath) MkdirAll(perm os.FileMode) error { + return os.MkdirAll(p.ToString(), perm) +} + +// Open implements os.Open(p) for an AbsoluteSystemPath +func (p AbsoluteSystemPath) Open() (*os.File, error) { + return os.Open(p.ToString()) +} + +// OpenFile implements os.OpenFile for an absolute path +func (p AbsoluteSystemPath) OpenFile(flags int, mode os.FileMode) (*os.File, error) { + return os.OpenFile(p.ToString(), flags, mode) +} + +// Lstat implements os.Lstat for absolute path +func (p AbsoluteSystemPath) Lstat() (os.FileInfo, error) { + return os.Lstat(p.ToString()) +} + +// Stat implements os.Stat for absolute path +func (p AbsoluteSystemPath) Stat() (os.FileInfo, error) { + return os.Stat(p.ToString()) +} + +// Findup checks all parent directories for a file. +func (p AbsoluteSystemPath) Findup(name RelativeSystemPath) (AbsoluteSystemPath, error) { + path, err := FindupFrom(name.ToString(), p.ToString()) + + return AbsoluteSystemPath(path), err + +} + +// Exists returns true if the given path exists. +func (p AbsoluteSystemPath) Exists() bool { + _, err := p.Lstat() + return err == nil +} + +// DirExists returns true if the given path exists and is a directory. +func (p AbsoluteSystemPath) DirExists() bool { + info, err := p.Lstat() + return err == nil && info.IsDir() +} + +// FileExists returns true if the given path exists and is a file. +func (p AbsoluteSystemPath) FileExists() bool { + info, err := os.Lstat(p.ToString()) + return err == nil && !info.IsDir() +} + +// ContainsPath returns true if this absolute path is a parent of the +// argument. +func (p AbsoluteSystemPath) ContainsPath(other AbsoluteSystemPath) (bool, error) { + // In Go, filepath.Rel can return a path that starts with "../" or equivalent. + // Checking filesystem-level contains can get extremely complicated + // (see https://github.com/golang/dep/blob/f13583b555deaa6742f141a9c1185af947720d60/internal/fs/fs.go#L33) + // As a compromise, rely on the stdlib to generate a relative path and then check + // if the first step is "../". + rel, err := filepath.Rel(p.ToString(), other.ToString()) + if err != nil { + return false, err + } + return !strings.HasPrefix(rel, _nonRelativeSentinel), nil +} + +// ReadFile reads the contents of the specified file +func (p AbsoluteSystemPath) ReadFile() ([]byte, error) { + return ioutil.ReadFile(p.ToString()) +} + +// VolumeName returns the volume of the specified path +func (p AbsoluteSystemPath) VolumeName() string { + return filepath.VolumeName(p.ToString()) +} + +// WriteFile writes the contents of the specified file +func (p AbsoluteSystemPath) WriteFile(contents []byte, mode os.FileMode) error { + return ioutil.WriteFile(p.ToString(), contents, mode) +} + +// EnsureDir ensures that the directory containing this file exists +func (p AbsoluteSystemPath) EnsureDir() error { + dir := p.Dir() + err := os.MkdirAll(dir.ToString(), _dirPermissions) + if err != nil && dir.FileExists() { + // It looks like this is a file and not a directory. Attempt to remove it; this can + // happen in some cases if you change a rule from outputting a file to a directory. + if err2 := dir.Remove(); err2 == nil { + err = os.MkdirAll(dir.ToString(), _dirPermissions) + } else { + return err + } + } + return err +} + +// MkdirAllMode Create directory at path and all necessary parents ensuring that path has the correct mode set +func (p AbsoluteSystemPath) MkdirAllMode(mode os.FileMode) error { + info, err := p.Lstat() + if err == nil { + if info.IsDir() && info.Mode() == mode { + // Dir exists with the correct mode + return nil + } else if info.IsDir() { + // Dir exists with incorrect mode + return os.Chmod(p.ToString(), mode) + } else { + // Path exists as file, remove it + if err := p.Remove(); err != nil { + return err + } + } + } + if err := os.MkdirAll(p.ToString(), mode); err != nil { + return err + } + // This is necessary only when umask results in creating a directory with permissions different than the one passed by the user + return os.Chmod(p.ToString(), mode) +} + +// Create is the AbsoluteSystemPath wrapper for os.Create +func (p AbsoluteSystemPath) Create() (*os.File, error) { + return os.Create(p.ToString()) +} + +// Ext implements filepath.Ext(p) for an absolute path +func (p AbsoluteSystemPath) Ext() string { + return filepath.Ext(p.ToString()) +} + +// RelativePathString returns the relative path from this AbsoluteSystemPath to another absolute path in string form as a string +func (p AbsoluteSystemPath) RelativePathString(path string) (string, error) { + return filepath.Rel(p.ToString(), path) +} + +// PathTo returns the relative path between two absolute paths +// This should likely eventually return an AnchoredSystemPath +func (p AbsoluteSystemPath) PathTo(other AbsoluteSystemPath) (string, error) { + return p.RelativePathString(other.ToString()) +} + +// Symlink implements os.Symlink(target, p) for absolute path +func (p AbsoluteSystemPath) Symlink(target string) error { + return os.Symlink(target, p.ToString()) +} + +// Readlink implements os.Readlink(p) for an absolute path +func (p AbsoluteSystemPath) Readlink() (string, error) { + return os.Readlink(p.ToString()) +} + +// Remove removes the file or (empty) directory at the given path +func (p AbsoluteSystemPath) Remove() error { + return os.Remove(p.ToString()) +} + +// RemoveAll implements os.RemoveAll for absolute paths. +func (p AbsoluteSystemPath) RemoveAll() error { + return os.RemoveAll(p.ToString()) +} + +// Base implements filepath.Base for an absolute path +func (p AbsoluteSystemPath) Base() string { + return filepath.Base(p.ToString()) +} + +// Rename implements os.Rename(p, dest) for absolute paths +func (p AbsoluteSystemPath) Rename(dest AbsoluteSystemPath) error { + return os.Rename(p.ToString(), dest.ToString()) +} + +// EvalSymlinks implements filepath.EvalSymlinks for absolute path +func (p AbsoluteSystemPath) EvalSymlinks() (AbsoluteSystemPath, error) { + result, err := filepath.EvalSymlinks(p.ToString()) + if err != nil { + return "", err + } + return AbsoluteSystemPath(result), nil +} + +// HasPrefix is strings.HasPrefix for paths, ensuring that it matches on separator boundaries. +// This does NOT perform Clean in advance. +func (p AbsoluteSystemPath) HasPrefix(prefix AbsoluteSystemPath) bool { + prefixLen := len(prefix) + pathLen := len(p) + + if prefixLen > pathLen { + // Can't be a prefix if longer. + return false + } else if prefixLen == pathLen { + // Can be a prefix if they're equal, but otherwise no. + return p == prefix + } + + // otherPath is definitely shorter than p. + // We need to confirm that p[len(otherPath)] is a system separator. + + return strings.HasPrefix(p.ToString(), prefix.ToString()) && os.IsPathSeparator(p[prefixLen]) +} |
