std.fs.path.Path
type pub inline Path
A path to a file or directory.
A Path
can be used to retrieve information about a path to a file or
directory such as the size or file type. Path
objects can be created by
either using Path.new
or by sending to_path
to a String
.
Examples
Creating a new Path
using a String
:
import std.fs.path (Path)
Path.new('/tmp/hello.txt')
Converting a String
to a Path
:
import std.fs.path (Path)
'/tmp/hello.txt'.to_path
Static methods
new
Show source codeHide source code
fn pub inline static new(path: String) -> Path {
Path(path)
}
fn pub inline static new(path: String) -> Path
Instance methods
!=
Show source codeHide source code
fn pub !=(other: T) -> Bool {
(self == other).false?
}
fn pub !=(other: T) -> Bool
Returns true
if self
and the given object are not equal to each other.
==
Show source codeHide source code
fn pub ==(other: ref Path) -> Bool {
@path == other.to_string
}
fn pub ==(other: ref Path) -> Bool
Returns true
if self
is equal to the given Path
.
Examples
Comparing two paths:
import std.fs.path (Path)
let path1 = Path.new('foo')
let path2 = Path.new('foo')
path1 == path2 # => true
absolute?
Show source codeHide source code
fn pub absolute? -> Bool {
absolute_path?(@path)
}
fn pub absolute? -> Bool
Returns true
if this Path
is an absolute path.
Examples
Checking if a Path
is absolute:
import std.fs.path (Path)
Path.new('foo').absolute? # => false
Path.new('/foo').absolute? # => true
clone
Show source codeHide source code
fn pub clone -> Path {
Path.new(@path)
}
fn pub clone -> Path
Creates a clone of self
.
components
Show source codeHide source code
fn pub components -> Components {
Components.new(self)
}
fn pub components -> Components
Returns an iterator over the components in self
.
When parsing the path as part of the iteration, the following normalization is applied:
- Repeated separators are treated as a single separator, such as
a/b
anda//b
produce the same components - Instances of
.
are normalized away except at the start, such thata/./b
anda/b
produce the same components - Trailing separators are removed, such that
a/b//
anda/b
produce the same components
If the path starts with the path separator (e.g. /
on Unix), the first
component returned by the iterator is the separator itself.
Examples
import std.fs.path (Path)
Path.new('a/b/c').components.to_array # => ['a', 'b', 'c']
Path.new('/a/b/c').components.to_array # => ['/', 'a', 'b', 'c']
copy
Show source codeHide source code
fn pub copy[T: ToString](to: ref T) -> Result[Int, Error] {
sys.copy_file(@path, to.to_string)
}
fn pub copy[T: ToString](to: ref T) -> Result[Int, Error]
Copies the file self
points to the file to
points to, returning the
number of bytes copied.
If the target file already exists, it's overwritten.
Errors
This method returns an Error
if the file couldn't be copied, such as when
the source file doesn't exist or the user lacks the necessary permissions.
Examples
import std.fs.file (WriteOnlyFile)
import std.fs.path (Path)
let path = Path.new('/tmp/test.txt')
let file = WriteOnlyFile.new(path).get
file.write_string('hello') # => Result.Ok(nil)
path.copy(to: '/tmp/test2.txt') # => Result.Ok(5)
create_directory
Show source codeHide source code
fn pub create_directory -> Result[Nil, Error] {
sys.create_directory(@path)
}
fn pub create_directory -> Result[Nil, Error]
Creates a new empty directory at the path self
points to.
Errors
This method returns an Error
if any of the following conditions are met:
- The user lacks the necessary permissions to create the directory.
2. The directory already exists.
Examples
import std.fs.path (Path)
Path.new('/tmp/test').create_directory # => Result.Ok(nil)
create_directory_all
Show source codeHide source code
fn pub create_directory_all -> Result[Nil, Error] {
# A common case is when all leading directories already exist, in which case
# we can avoid the more expensive loop to create the intermediate
# directories.
match create_directory {
case Ok(_) or Error(AlreadyExists) -> return Result.Ok(nil)
case Error(NotFound) -> {}
case Error(e) -> throw e
}
try components.try_reduce('', fn (leading, cur) {
let path = join_strings(leading, cur)
match sys.create_directory(path) {
case Ok(_) or Error(AlreadyExists) -> Result.Ok(path)
case Error(e) -> Result.Error(e)
}
})
Result.Ok(nil)
}
fn pub create_directory_all -> Result[Nil, Error]
Creates a new empty directory at the path self
points to, while also
creating any intermediate directories.
Unlike Path.create_directory
, this method doesn't return an Error
if
any of the directories already exist.
Errors
This method returns an Error
if any of the directories can't be created,
such as when the user doesn't have the required permissions to do so.
Examples
import std.fs.path (Path)
Path.new('/tmp/foo/bar/test').create_directory_all # => Result.Ok(nil)
directory
Show source codeHide source code
fn pub directory -> Path {
let buf = StringBuffer.new
let comp = Components.new(self).peekable
let mut root = false
loop {
match comp.next {
case Some(SEPARATOR) -> {
root = true
buf.push(SEPARATOR)
}
case Some(v) if comp.peek.some? -> {
let any = buf.size > 0
if any and root { root = false } else if any { buf.push(SEPARATOR) }
buf.push(v)
}
case _ -> break
}
}
if buf.size == 0 { Path.new('.') } else { Path.new(buf.into_string) }
}
fn pub directory -> Path
Returns a Path
to the directory of the current Path
.
This method does not touch the filesystem, and thus does not resolve paths
like ..
and symbolic links to their real paths.
This method normalizes the returned Path
similar to Path.components
.
Refer to the documentation of Path.components
for more details.
Examples
Obtaining the directory of a path:
import std.fs.path (Path)
Path.new('/foo/bar').directory # => Path.new('/foo')
Obtaining the directory of the root directory:
import std.fs.path (Path)
Path.new('/').directory # Path.new('/')
directory?
Show source codeHide source code
fn pub directory? -> Bool {
metadata.map(fn (m) { m.type.directory? }).or(false)
}
fn pub directory? -> Bool
Returns true
if the path points to a directory.
exists?
Show source codeHide source code
fn pub exists? -> Bool {
metadata.ok?
}
fn pub exists? -> Bool
Returns true
if the path points to a file/directory/etc that exists.
expand
Show source codeHide source code
fn pub expand -> Result[Path, Error] {
if @path == HOME {
return match home_directory {
case Some(v) -> Result.Ok(v)
case _ -> Result.Error(Error.NotFound)
}
}
let mut target = @path
match @path.strip_prefix(HOME_WITH_SEPARATOR) {
case Some(tail) -> {
target = match home_directory {
case Some(v) -> join_strings(v.path, tail)
case _ -> throw Error.NotFound
}
}
case _ -> {}
}
sys.expand_path(target).map(fn (v) { Path.new(v) })
}
fn pub expand -> Result[Path, Error]
Returns the canonical, absolute version of self
.
Resolving home directories
If self
is equal to ~
, this method returns the path to the user's home
directory. If self
starts with ~/
, this prefix is replaced with the path
to the user's home directory (e.g. ~/foo
becomes /var/home/alice/foo
).
Errors
This method may return an Error
for cases such as:
self
doesn't exist- a component that isn't the last component is not a directory
self
is equal to~
or starts with~/
, but the home directory can't be found (e.g. it doesn't exist)
Examples
import std.fs.path (Path)
Path.new('/foo/../bar').expand.get # => Path.new('/bar')
Path.new('~').expand.get # => '/var/home/...'
Path.new('~/').expand.get # => '/var/home/...'
extension
Show source codeHide source code
fn pub extension -> Option[String] {
let size = @path.size
let mut min = match bytes_before_last_separator(@path) {
case -1 -> 0
case n -> n + 1
}
if min >= size { return Option.None }
# If the name starts with a dot, we work our way backwards until the _next_
# byte. This way we don't treat `.foo` as having the extension `foo`.
if @path.byte(min) == DOT_BYTE { min += 1 }
let max = size - 1
let mut idx = max
# We consider something an extension if it has at least one non-dot byte,
# meaning `foo.` is a path _without_ an extension. Different languages
# handle this differently:
#
# Language Path Extension Leading dot included
# ---------------------------------------------------------
# Elixir 'foo.' '.' Yes
# Go 'foo.' '.' Yes
# Node.js 'foo.' '.' Yes
# Python 'foo.' NONE No
# Ruby 'foo.' '.' Yes
# Rust 'foo.' NONE No
# Vimscript 'foo.' NONE No
#
# Things get more inconsistent for paths such as `...`, with some treating
# it as a file called `..` with the extension `.`, while others consider it
# a path without an extension.
while idx > min {
if @path.byte(idx) == DOT_BYTE { break } else { idx -= 1 }
}
if idx < max and idx > min {
Option.Some(@path.slice(idx + 1, size).into_string)
} else {
Option.None
}
}
fn pub extension -> Option[String]
Returns the file extension of this path (without the leading .
), if there
is any.
Examples
import std.fs.path (Path)
Path.new('foo.txt').extension # => Option.Some('txt')
Path.new('foo').extension # => Option.None
file?
Show source codeHide source code
fn pub file? -> Bool {
metadata.map(fn (m) { m.type.file? }).or(false)
}
fn pub file? -> Bool
Returns true
if the path points to a file.
fmt
Show source codeHide source code
fn pub fmt(formatter: mut Formatter) {
@path.fmt(formatter)
}
fn pub fmt(formatter: mut Formatter)
Formats self
in a human-readable format for debugging purposes.
hash
Show source codeHide source code
fn pub hash[H: mut + Hasher](hasher: mut H) {
@path.hash(hasher)
}
fn pub hash[H: mut + Hasher](hasher: mut H: mut)
Writes the hash for self
into the given Hasher
.
into_string
Show source codeHide source code
fn pub move into_string -> String {
@path
}
fn pub move into_string -> String
Moves self
into a String
.
join
Show source codeHide source code
fn pub join[T: ToString](path: ref T) -> Path {
Path.new(join_strings(@path, with: path.to_string))
}
fn pub join[T: ToString](path: ref T) -> Path
Joins self
and the given path together to form a new Path
.
Examples
Joining a Path
with a String
:
import std.fs.path (Path)
Path.new('foo/bar').join('baz').to_string # => 'foo/bar/baz'
Joining a Path
with another Path
:
import std.fs.path (Path)
Path.new('foo/bar').join(Path.new('bar')).to_string # => 'foo/bar/baz'
list
Show source codeHide source code
fn pub list -> Result[ReadDirectory, Error] {
sys.ReadDirectory.new(@path).map(fn (inner) {
ReadDirectory(path: @path, inner: inner)
})
}
fn pub list -> Result[ReadDirectory, Error]
Returns an iterator yielding the entries in the directory self
points to.
The iterator yields values of type Result[DirectoryEntry, Error]
, as
errors may be produced during iteration (e.g. file permissions are changed
such that we can no longer read the directory contents).
Errors
This method returns an Error
if any of the following conditions are met:
- The user lacks the necessary permissions to read the contents of the directory.
2. The path isn't a valid directory (i.e. it's a file or doesn't exist).
Examples
This prints the files in the current working directory while ignoring directories:
import std.fs.path (Path)
import std.stdio (Stdout)
let out = Stdout.new
let path = Path.new('.')
let iter = path.list.or_panic('failed to create the iterator')
iter.each fn (result) {
match result {
case Ok({ @path = path, @type = File }) -> {
out.print(path.to_string)
nil
}
case Ok(_) -> {}
case Error(err) -> panic(err.to_string)
}
}
list_all
Show source codeHide source code
fn pub list_all -> Result[Stream[Result[DirectoryEntry, Error]], Error] {
list.map(fn (iter) {
let dirs = []
let mut current = iter
Stream.new(fn move {
loop {
match current.next {
case Some(Ok({ @path = p, @type = Directory })) -> dirs.push(p)
case Some(Ok(entry)) -> return Option.Some(Result.Ok(entry))
case Some(Error(e)) -> return Option.Some(Result.Error(e))
case None -> {
match dirs.pop {
case Some(dir) -> {
match dir.list {
case Ok(iter) -> current = iter
case Error(e) -> return Option.Some(Result.Error(e))
}
}
case _ -> return Option.None
}
}
}
}
})
})
}
fn pub list_all -> Result[Stream[Result[DirectoryEntry, Error]], Error]
Returns an iterator that yields all non-directory entries in self
and in
any sub directories.
The order in which entries are returned is unspecified and shouldn't be relied upon, and may change at any given point.
If this iterator fails to read a sub directory (e.g. bar
in ./foo/bar
isn't readable) a Some(Error(std.io.Error))
is returned. Because a Some
is returned the iterator can advance when encountering an error, similar to
the iterator returned by Path.list
.
Examples
import std.fs.path (Path)
Path.new('/tmp').list_all.get.next
# => Option.Some(Result.Ok(DirectoryEntry(...)))
metadata
Show source codeHide source code
fn pub metadata -> Result[Metadata, Error] {
sys.path_metadata(@path)
}
fn pub metadata -> Result[Metadata, Error]
Returns a metadata about the current path, such as its size and creation time.
Errors
This method returns an Error
if the underlying system call fails, such as
when the file no longer exists.
Examples
import std.fs.path (Path)
Path
.new('/tmp/test.txt')
.metadata
.or_panic('failed to get the metadata')
.size # => 1234
relative?
Show source codeHide source code
fn pub relative? -> Bool {
absolute?.false?
}
fn pub relative? -> Bool
Returns true
if this Path
is a relative path.
Examples
Checking if a Path
is relative:
import std.fs.path (Path)
Path.new('foo').relative? # => true
Path.new('../').relative? # => true
Path.new('/foo').relative? # => false
remove_directory
Show source codeHide source code
fn pub remove_directory -> Result[Nil, Error] {
sys.remove_directory(@path)
}
fn pub remove_directory -> Result[Nil, Error]
Removes the directory self
points to.
Errors
This method returns an error if self
points to a file or if the directory
can't be removed (e.g. the user lacks the necessary permissions).
Examples
import std.fs.path (Path)
let path = Path.new('/tmp/foo')
path.create_directory # => Result.Ok(nil)
path.remove_directory # => Result.Ok(nil)
remove_directory_all
Show source codeHide source code
fn pub remove_directory_all -> Result[Nil, Error] {
let stack = [@path]
let dirs = [@path]
# First we remove all the files and gather the directories that need to be
# removed.
loop {
let dir = match stack.pop {
case Some(v) -> v
case _ -> break
}
let iter = try sys.ReadDirectory.new(dir)
try iter.try_each(fn (entry) {
match entry {
case Ok((name, Directory)) -> {
let path = join_strings(dir, name)
stack.push(path)
dirs.push(path)
}
case Ok((name, _)) -> try sys.remove_file(join_strings(dir, name))
case Error(e) -> throw e
}
Result.Ok(nil)
})
}
# Now we can remove the directories in a depth-first order.
loop {
match dirs.pop {
case Some(v) -> try sys.remove_directory(v)
case _ -> break
}
}
Result.Ok(nil)
}
fn pub remove_directory_all -> Result[Nil, Error]
Removes the directory and its contents self
points to.
When encountering symbolic links, the link itself is removed instead of the file it points to.
Errors
This method returns an enty if any of the directories or the contents can't
be removed, such as when the user lacks the necessary permissions, or if
self
points to something other than a directory.
Examples
Removing a directory:
import std.fs.path (Path)
Path.new('/tmp/foo/bar').create_directory_all # => Result.Ok(nil)
Path.new('/tmp/foo').remove_directory_all # => Result.Ok(nil)
remove_file
Show source codeHide source code
fn pub remove_file -> Result[Nil, Error] {
sys.remove_file(@path)
}
fn pub remove_file -> Result[Nil, Error]
Removes the file self
points to.
Errors
This method returns an Error
if the file self
points to can't be removed
(e.g. it doesn't exist) or isn't a file.
Examples
import std.fs.file (WriteOnlyFile)
import std.fs.path (Path)
let path = Path.new('/tmp/test.txt')
let handle = WriteOnlyFile.new(path).get
handle.write_string('hello') # => Result.Ok(nil)
path.remove_file # => Result.Ok(nil)
strip_prefix
Show source codeHide source code
fn pub strip_prefix(prefix: ref Path) -> Option[Path] {
let comp = components
let valid = prefix.components.all?(fn (theirs) {
match comp.next {
case Some(ours) -> ours == theirs
case _ -> false
}
})
if valid {
Option.Some(Path.new(String.join(comp, SEPARATOR)))
} else {
Option.None
}
}
fn pub strip_prefix(prefix: ref Path) -> Option[Path]
Returns a new Path
with the prefix prefix
removed from it.
If self
doesn't start with prefix
, a None
is returned.
This method operates on the in-memory representation of self
, and doesn't
expand the path, follow symbolic links, etc.
Examples
import std.fs.path (Path)
Path.new('a/b').strip_prefix(Path.new('a')) # => Path.new('b')
Path.new('/a/b').strip_prefix(Path.new('/a')) # => Path.new('b')
tail
Show source codeHide source code
fn pub tail -> String {
let comp = Components.new(self)
let mut start = -1
let mut size = 0
# This finds the range of the last component, taking into account path
# normalization.
while comp.index < comp.size {
comp.advance_separator
if comp.index < comp.size {
start = comp.index
size = 0
}
while comp.index < comp.size and comp.separator?(comp.index).false? {
comp.index += 1
size += 1
}
}
if start == -1 { '' } else { @path.slice(start, size).into_string }
}
fn pub tail -> String
Returns the last component in self
.
If self
is a file, then the tail will be the file name including its
extension. If self
is a directory, the directory name is returned.
Examples
import std.fs.path (Path)
Path.new('foo/bar/baz.txt') # => 'baz.txt'
to_string
Show source codeHide source code
fn pub to_string -> String {
@path
}
fn pub to_string -> String
Converts a Path
to a String
.
Examples
Converting a Path
:
import std.fs.path (Path)
let path = Path.new('/dev/null')
path.to_string # => '/dev/null'
with_extension
Show source codeHide source code
fn pub with_extension(name: String) -> Path {
if name.contains?(SEPARATOR) {
panic("file extensions can't contain path separators")
}
if @path.empty? { return clone }
let raw = match extension {
case Some(v) if name.empty? -> {
@path.slice(start: 0, size: @path.size - v.size - 1).into_string
}
case Some(v) -> {
'${@path.slice(start: 0, size: @path.size - v.size - 1)}.${name}'
}
case _ if name.empty? or @path.ends_with?(SEPARATOR) -> @path
case _ -> '${@path}.${name}'
}
Path.new(raw)
}
fn pub with_extension(name: String) -> Path
Returns a copy of self
with the given extension.
If self
already has an extension, it's overwritten the given extension. If
the given extension is an empty String
, the new Path
contains no
extension.
Panics
This method panics if the extension contains a path separator.
Examples
import std.fs.path (Path)
Path.new('a').with_extension('txt') # => Path.new('a.txt')
Path.new('a.txt').with_extension('md') # => Path.new('a.md')
Implemented traits
Clone
impl Clone[Path] for Path
Equal
impl Equal[ref Path] for Path
Format
impl Format for Path
Hash
impl Hash for Path
IntoString
impl IntoString for Path
ToString
impl ToString for Path