blob: 7cbb5240f4812ace5b54e4ee818708867048c5a4 [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package file
import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)
// Path is a clean absolute path with platform specific separators.
type Path struct{ value string }
// Abs is the primary constructor of new Path objects from strings using either the / or system separator.
func Abs(path string) Path {
abs, err := filepath.Abs(filepath.FromSlash(path))
if err != nil {
return Path{path}
}
return Path{filepath.Clean(abs)}
}
// ExecutablePath returns the path to the running executable.
func ExecutablePath() Path {
path := Abs(os.Args[0])
if path.Exists() {
return path
}
path, err := FindExecutable(os.Args[0])
if err != nil {
panic(err)
}
return path
}
// FindExecutable searches the system search path for the named binary, and returns a non empty Path if found.
func FindExecutable(name string) (Path, error) {
path, err := exec.LookPath(name)
return Abs(path), err
}
// IsEmpty returns true if the path has no value
func (p Path) IsEmpty() bool { return p.value == "" }
// System returns the full absolute path using the system separator.
func (p Path) System() string { return p.value }
// Slash returns the full absolute path using the / separator.
func (p Path) Slash() string { return filepath.ToSlash(p.value) }
// The default string form uses the system representation.
func (p Path) String() string { return p.value }
// Set assigns the value of the path.
// This conforms to the flag variable interface.
func (p *Path) Set(value string) error {
*p = Abs(value)
return nil
}
// Parent returns the parent directory of the path, if it has one.
// It will return the canonical version of the path if it is a root.
func (p Path) Parent() Path { return Path{filepath.Dir(p.value)} }
// Basename returns the name part of the path (without directories).
func (p Path) Basename() string { return filepath.Base(p.value) }
// Ext returns the extension of the path, including the '.', or an empty string
// if the path has no extension.
func (p Path) Ext() string { return filepath.Ext(p.value) }
// Split returns the parent and name of the path.
func (p Path) Split() (Path, string) {
dir, name := filepath.Split(p.value)
return Path{dir}, name
}
// SplitExt returns the path excluding the file extension and the file
// extension. The extension includes the '.'.
func (p Path) SplitExt() (Path, string) {
ext := p.Ext()
return Path{p.value[:len(p.value)-len(ext)]}, ext
}
// Join returns a path formed from joining this base with a child path.
// If there are any / characters in the strings, they will be converted to they system separator.
func (p Path) Join(join ...string) Path {
if len(join) == 0 {
return p
}
trailing := filepath.Join(join...)
trailing = filepath.FromSlash(trailing)
return Path{filepath.Clean(filepath.Join(p.value, trailing))}
}
// Files reads the list of files in path.
func (p Path) Files() PathList {
infos, _ := ioutil.ReadDir(p.value)
list := PathList{}
for _, i := range infos {
if !i.IsDir() {
list = append(list, Path{filepath.Join(p.value, i.Name())})
}
}
return list
}
// Glob adds each pattern to the path in turn, and uses filepath.Glob to resolve that pattern to a list of files.
func (p Path) Glob(pattern ...string) PathList {
list := PathList{}
for _, test := range pattern {
matches, err := filepath.Glob(filepath.Join(p.value, test))
if err != nil {
return PathList{}
}
for _, match := range matches {
list = append(list, Path{match})
}
}
return list
}
// Info returns the file information for ths path.
func (p Path) Info() os.FileInfo {
info, _ := os.Stat(p.value)
return info
}
// Exists returns true if this File exists.
func (p Path) Exists() bool {
return p.Info() != nil
}
// Timestamp returns the last modified time reported by the file system.
func (p Path) Timestamp() time.Time {
info := p.Info()
if info == nil || info.IsDir() {
return time.Time{}
}
return info.ModTime()
}
// Contains returns true if this directory contains the other.
func (p Path) Contains(other Path) bool {
if len(p.value) >= len(other.value) {
return false
}
return strings.HasPrefix(other.value, p.value)
}
// RelativeTo returns the path of this File relative to base.
func (p Path) RelativeTo(base Path) (string, error) {
return filepath.Rel(base.value, p.value)
}
// ChangeExt returns a new Path with the extension changed to ext.
// ext should being with a '.' to change extension, or be an empty string to
// remove the extension.
func (p Path) ChangeExt(ext string) Path {
prev := filepath.Ext(p.value)
return Path{p.value[:len(p.value)-len(prev)] + ext}
}
// Matches returns true if the Path matches any of the patterns.
func (p Path) Matches(patterns ...string) bool {
for _, pattern := range patterns {
matched, _ := filepath.Match(pattern, p.Basename())
if matched {
return true
}
}
return false
}