std.fs.path.Path
type pub inline PathA 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) -> PathInstance methods
!=
Show source codeHide source code
fn pub !=(other: ref Self) -> Bool {
!(self == other)
}fn pub !=(other: ref Self) -> BoolReturns 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) -> BoolReturns 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? -> BoolReturns 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 -> PathCreates a clone of self.
The returned value is an owned value that is the same type as the receiver
of this method. For example, cloning a ref Array[Int] results in a
Array[Int], not another ref Array[Int].
components
Show source codeHide source code
fn pub components -> Components {
Components.new(self)
}fn pub components -> ComponentsReturns 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/banda//bproduce the same components - Instances of
.are normalized away except at the start, such thata/./banda/bproduce the same components - Trailing separators are removed, such that
a/b//anda/bproduce 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('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.to_string)
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 = ByteArray.new
let comp = Components.new(self).peekable
let mut root = false
loop {
match comp.next {
case Some('/') -> {
root = true
buf.append(SEPARATOR)
}
case Some(v) if comp.peek.some? -> {
let any = !buf.empty?
if any and root { root = false } else if any { buf.append(SEPARATOR) }
buf.append(v)
}
case _ -> break
}
}
if buf.empty? { Path.new('.') } else { Path.new(buf.into_string) }
}fn pub directory -> PathReturns 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? -> BoolReturns true if the path points to a directory.
exists?
Show source codeHide source code
fn pub exists? -> Bool {
metadata.ok?
}fn pub exists? -> BoolReturns 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:
selfdoesn't exist- a component that isn't the last component is not a directory
selfis 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[Slice[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.get(min).or_panic == 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.get(idx).or_panic == DOT_BYTE { break } else { idx -= 1 }
}
if idx < max and idx > min {
Option.Some(@path.slice(start: idx + 1, end: size))
} else {
Option.None
}
}fn pub extension -> Option[Slice[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? -> BoolReturns 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 -> StringMoves 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) -> PathJoins 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_with('failed to create the iterator')
for result in iter {
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_with('failed to get the metadata')
.size # => 1234
relative?
Show source codeHide source code
fn pub relative? -> Bool {
!absolute?
}fn pub relative? -> BoolReturns 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
}
for entry in try sys.ReadDirectory.new(dir) {
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
}
}
}
# 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('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 -> Slice[String] {
let comp = Components.new(self)
let mut start = -1
let mut end = -1
# 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
end = start
}
while comp.index < comp.size and !comp.separator?(comp.index) {
comp.index += 1
end += 1
}
}
if start == -1 { @path.slice(0, 0) } else { @path.slice(start, end: end) }
}fn pub tail -> Slice[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 -> StringConverts 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, end: @path.size - v.size - 1).to_string
}
case Some(v) -> {
'${@path.slice(start: 0, end: @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) -> PathReturns 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 for PathEqual
impl Equal for PathFormat
impl Format for PathHash
impl Hash for PathIntoString
impl IntoString for PathToString
impl ToString for Path