Module docstring
{}
{}
System.FilePath
structure
/--
A path on the file system.
Paths consist of a sequence of directories followed by the name of a file or directory. They are
delimited by a platform-dependent separator character (see `System.FilePath.pathSeparator`).
-/
structure FilePath where
/-- The string representation of the path. -/
toString : String
deriving Inhabited, DecidableEq, Hashable
System.instInhabitedFilePath
instance
: Inhabited✝ (@System.FilePath)
Inhabited, DecidableEq, Hashable
System.instDecidableEqFilePath
instance
: DecidableEq✝ (@System.FilePath✝)
DecidableEq, Hashable
System.instHashableFilePath
instance
: Hashable✝ (@System.FilePath✝)
Hashable
System.instReprFilePath
instance
: Repr FilePath
System.instToStringFilePath
instance
: ToString FilePath
System.FilePath.pathSeparator
definition
: Char
/--
The character that separates directories.
On platforms that support multiple separators, `System.FilePath.pathSeparator` is the “ideal” one expected by users
on the platform. `System.FilePath.pathSeparators` lists all supported separators.
-/
def pathSeparator : Char :=
if isWindows then '\\' else '/'
System.FilePath.pathSeparators
definition
: List Char
/--
The list of all path separator characters supported on the current platform.
On platforms that support multiple separators, `System.FilePath.pathSeparator` is the “ideal” one
expected by users on the platform.
-/
def pathSeparators : List Char :=
if isWindows then ['\\', '/'] else ['/']
System.FilePath.extSeparator
definition
: Char
/--
The character that separates file extensions from file names.
-/
def extSeparator : Char := '.'
System.FilePath.exeExtension
definition
: String
/--
The file extension expected for executable binaries on the current platform, or `""` if there is no
such extension.
-/
def exeExtension : String :=
if isWindows then "exe" else ""
System.FilePath.normalize
definition
(p : FilePath) : FilePath
/--
Normalizes a path, returning an equivalent path that may better follow platform conventions.
In particular:
* On Windows, drive letters are made uppercase.
* On platforms that support multiple path separators (that is, where
`System.FilePath.pathSeparators` has length greater than one), alternative path separators are
replaced with the preferred path separator.
There is no guarantee that two equivalent paths normalize to the same path.
-/
-- TODO: normalize `a/`, `a//b`, etc.
def normalize (p : FilePath) : FilePath := Id.run do
let mut p := p
-- normalize drive letter
if isWindows && p.toString.length >= 2 && (p.toString.get 0).isLower && p.toString.get ⟨1⟩ == ':' then
p := ⟨p.toString.set 0 (p.toString.get 0).toUpper⟩
-- normalize separator
unless pathSeparators.length == 1 do
p := ⟨p.toString.map fun c => if pathSeparators.contains c then pathSeparator else c⟩
return p
System.FilePath.isAbsolute
definition
(p : FilePath) : Bool
/--
An absolute path starts at the root directory or a drive letter. Accessing files through an absolute
path does not depend on the current working directory.
-/
def isAbsolute (p : FilePath) : Bool :=
pathSeparatorspathSeparators.contains p.toString.front || (isWindows && p.toString.length > 1 && p.toString.iter.next.curr == ':')
System.FilePath.isRelative
definition
(p : FilePath) : Bool
/--
A relative path is one that depends on the current working directory for interpretation. Relative
paths do not start with the root directory or a drive letter.
-/
def isRelative (p : FilePath) : Bool :=
!p.isAbsolute
System.FilePath.join
definition
(p sub : FilePath) : FilePath
/--
Appends two paths, taking absolute paths into account. This operation is also accessible via the `/`
operator.
If `sub` is an absolute path, then `p` is discarded and `sub` is returned. If `sub` is a relative
path, then it is attached to `p` with the platform-specific path separator.
-/
def join (p sub : FilePath) : FilePath :=
if sub.isAbsolute then
sub
else
⟨p.toString ++ pathSeparator.toString ++ sub.toString⟩
System.FilePath.instDiv
instance
: Div FilePath
instance : Div FilePath where
div := FilePath.join
System.FilePath.instHDivString
instance
: HDiv FilePath String FilePath
System.FilePath.parent
definition
(p : FilePath) : Option FilePath
/--
Returns the parent directory of a path, if there is one.
If the path is that of the root directory or the root of a drive letter, `none` is returned.
Otherwise, the path's parent directory is returned.
-/
def parent (p : FilePath) : Option FilePath :=
let extractParentPath := FilePath.mkFilePath.mk <$> p.toString.extract {} <$> posOfLastSep p
if p.isAbsolute then
let lengthOfRootDirectory := if pathSeparators.contains p.toString.front then 1 else 3
if p.toString.length == lengthOfRootDirectory then
-- `p` is a root directory
none
else if posOfLastSep p == String.Pos.mk (lengthOfRootDirectory - 1) then
-- `p` is a direct child of the root
some ⟨p.toString.extract 0 ⟨lengthOfRootDirectory⟩⟩
else
-- `p` is an absolute path with at least two subdirectories
extractParentPath
else
-- `p` is a relative path
extractParentPath
System.FilePath.fileName
definition
(p : FilePath) : Option String
/--
Extracts the last element of a path if it is a file or directory name.
Returns `none ` if the last entry is a special name (such as `.` or `..`) or if the path is the root
directory.
-/
def fileName (p : FilePath) : Option String :=
let lastPart := match posOfLastSep p with
| somesome sepPos => p.toString.extract (sepPos + '/') p.toString.endPos
| none => p.toString
if lastPart.isEmpty || lastPart == "." || lastPart == ".." then none else some lastPart
System.FilePath.fileStem
definition
(p : FilePath) : Option String
/--
Extracts the stem (non-extension) part of `p.fileName`.
If the filename contains multiple extensions, then only the last one is removed. Returns `none` if
there is no file name at the end of the path.
Examples:
* `("app.exe" : System.FilePath).fileStem = some "app"`
* `("file.tar.gz" : System.FilePath).fileStem = some "file.tar"`
* `("files/" : System.FilePath).fileStem = none`
* `("files/picture.jpg" : System.FilePath).fileStem = some "picture"`
-/
def fileStem (p : FilePath) : Option String :=
p.fileName.map fun fname =>
match fname.revPosOf '.' with
| somesome ⟨0⟩ => fname
| somesome pos => fname.extract 0 pos
| none => fname
System.FilePath.extension
definition
(p : FilePath) : Option String
/--
Extracts the extension part of `p.fileName`.
If the filename contains multiple extensions, then only the last one is extracted. Returns `none` if
there is no file name at the end of the path.
Examples:
* `("app.exe" : System.FilePath).extension = some "exe"`
* `("file.tar.gz" : System.FilePath).extension = some "gz"`
* `("files/" : System.FilePath).extension = none`
* `("files/picture.jpg" : System.FilePath).extension = some "jpg"`
-/
def extension (p : FilePath) : Option String :=
p.fileName.bind fun fname =>
match fname.revPosOf '.' with
| somesome 0 => none
| somesome pos => fname.extract (pos + '.') fname.endPos
| none => none
System.FilePath.withFileName
definition
(p : FilePath) (fname : String) : FilePath
/--
Replaces the file name at the end of the path `p` with `fname`, placing `fname` in the parent
directory of `p`.
If `p` has no parent directory, then `fname` is returned unmodified.
-/
def withFileName (p : FilePath) (fname : String) : FilePath :=
match p.parent with
| none => ⟨fname⟩
| somesome p => p / fname
System.FilePath.addExtension
definition
(p : FilePath) (ext : String) : FilePath
/--
Appends the extension `ext` to a path `p`.
`ext` should not have leading `.`, as this function adds one. If `ext` is the empty string, no
`.` is added.
Unlike `System.FilePath.withExtension`, this does not remove any existing extension.
-/
def addExtension (p : FilePath) (ext : String) : FilePath :=
match p.fileName with
| none => p
| somesome fname => p.withFileName (if ext.isEmpty then fname else fname ++ "." ++ ext)
System.FilePath.withExtension
definition
(p : FilePath) (ext : String) : FilePath
/--
Replaces the current extension in a path `p` with `ext`, adding it if there is no extension. If the
path has multiple file extensions, only the last one is replaced. If the path has no filename, or if
`ext` is the empty string, then the filename is returned unmodified.
`ext` should not have a leading `.`, as this function adds one.
Examples:
* `("files/picture.jpeg" : System.FilePath).withExtension "jpg" = ⟨"files/picture.jpg"⟩`
* `("files/" : System.FilePath).withExtension "zip" = ⟨"files/"⟩`
* `("files" : System.FilePath).withExtension "zip" = ⟨"files.zip"⟩`
* `("files/archive.tar.gz" : System.FilePath).withExtension "xz" = ⟨"files.tar.xz"⟩`
-/
def withExtension (p : FilePath) (ext : String) : FilePath :=
match p.fileStem with
| none => p
| somesome stem => p.withFileName (if ext.isEmpty then stem else stem ++ "." ++ ext)
System.FilePath.components
definition
(p : FilePath) : List String
/--
Splits a path into a list of individual file names at the platform-specific path separator.
-/
def components (p : FilePath) : List String :=
p.normalize |>.toString.splitOn pathSeparator.toString
System.mkFilePath
definition
(parts : List String) : FilePath
/--
Constructs a path from a list of file names by interspersing them with the current platform's path
separator.
-/
def mkFilePath (parts : List String) : FilePath :=
⟨String.intercalate FilePath.pathSeparator.toString parts⟩
System.instCoeStringFilePath
instance
: Coe String FilePath
instance : Coe String FilePath where
coe := FilePath.mk
System.SearchPath
abbrev
abbrev SearchPath := List FilePath
System.SearchPath.separator
definition
: Char
/--
The character that is used to separate the entries in the `$PATH` (or `%PATH%`) environment variable.
This value is platform dependent.
-/
protected def separator : Char :=
if isWindows then ';' else ':'
System.SearchPath.parse
definition
(s : String) : SearchPath
/--
Separates the entries in the `$PATH` (or `%PATH%`) environment variable by the current
platform-dependent separator character.
-/
def parse (s : String) : SearchPath :=
s.split (fun c => SearchPath.separator == c) |>.map FilePath.mk
System.SearchPath.toString
definition
(path : SearchPath) : String
/--
Joins a list of paths into a suitable value for the current platform's `$PATH` (or `%PATH%`)
environment variable.
-/
def toString (path : SearchPath) : String :=
SearchPath.separator.toString.intercalate (path.map FilePath.toString)