14.1 Paths
On this page:
Path
Path  String
Path  String.to_  path
Path  String.to_  absolute_  path
Path.Absolute
Path.Relative
Path.Drive  Relative
Path.Directory
Path.Element
Path.Dot
Path.Dot.same
Path.Dot.up
Path.like
Path
Path
Path.current_  directory
Path.bytes
Path.string
Path.convention
Path.add
+  /
Path.split
Path.name
Path.parent
Path.directory_  only
Path.to_  directory_  path
Path.to_  absolute_  path
Path.suffix
Path.replace_  suffix
Path.add_  suffix
Path.cleanse
Path.simplify
Path.normal_  case
Path.as_  relative_  to
14.1.1 Runtime Paths
def
8.15.0.12

14.1 Paths🔗ℹ

A path value represents a filesystem path for the host operating system. A cross-platform path is a generalization of a path, and most path operations also accept cross-platform paths, but they produce specifically paths when given paths.

Paths are comparable, which means that generic operations like < and > work on paths.

annotation

Path

 

annotation

PathString

 

annotation

PathString.to_path

 

annotation

PathString.to_absolute_path

 

annotation

PathString.to_absolute_path(~relative_to: base_expr)

 

annotation

Path.Absolute

 

annotation

Path.Relative

 

annotation

Path.DriveRelative

 

annotation

Path.Directory

 

annotation

Path.Element

 

enumeration

enum Path.Dot:

  same

  up

 

annotation

Path.like(expr)

Matches a path value. The PathString annotation allows ReadableString as well as Path values. The PathString.to_path converter annotation allows PathString values, but converts ReadableString values to Path values. Similarly PathString.to_absolute_path is a converter annotation that converts a PathString into an absolute path relative to Path.current_directory() or to the directory base_expr, where base_expr must satisfy (PathString.to_path && Path.Absolute) || CrossPath.Absolute.

The Path.Absolute annotation only matches Paths that begin with a root directory or drive. The Path.Relative annotation only matches Paths that are relative to some base directory. The Path.DriveRelative annotation only matches Windows Paths that are relative to a drive.

The Path.Directory annotation only matches Paths that syntactically refer to a directory; see filesystem.directory_exists for checking whether a path refers to a directory on the filesystem.

The Path.Element annotation matches a path that represents a single path element—that is, a path for which Path.split returns a list containing one element.

A Path.Dot symbols refer to an abstract directory-navigation path element. For both path conventions currently supported, the symbol #'same as a Path.Dot is equivalent to ".", and the symbol #'same as a Path.Dot is equivalent to "..".

A Path.like(expr) annotation is satisfied by a cross-platform path with the same convention as the result of expr, where the result of expr must satisfy PathString || CrossPath || Path.Dot, and a PathString or Path.Dot implies the current platform’s convention.

function

fun Path(path :: Bytes || PathString || Path.Dot) :: Path

Constructs a path given a byte string, string, existing path, or directory-navigation symbol. When a path is provided as path, then the result is path.

> def p = Path("/home/rhombus/shape.txt")

> p

Path("/home/rhombus/shape.txt")

> Path(p)

Path("/home/rhombus/shape.txt")

> p.string()

"/home/rhombus/shape.txt"

binding operator

Path(bind)

Matches a path where the byte-string form of the path matches bind.

> def Path(bstr) = Path("/home/rhombus/shape.txt")

> bstr

#"/home/rhombus/shape.txt"

A context parameter for the current directory. This directory need not actually exist on the filesystem.

Converts a path to a byte-string form, which does not lose any information about the path.

> def p = Path("/home/rhombus/shape.txt")

> Path.bytes(p)

#"/home/rhombus/shape.txt"

> p.bytes()

#"/home/rhombus/shape.txt"

Converts a path to a human-readable form, but the conversion may lose information if the path cannot be expressed using a string (e.g., due to a byte-string form that is not a UTF-8 encoding).

> def p = Path(#"/home/rhombus/shape.txt")

> Path.string(p)

"/home/rhombus/shape.txt"

> p.string()

"/home/rhombus/shape.txt"

Reports the convention of a path. For a path that satisfies Path, the result is the same as CrossPath.Convention.current().

> def p = CrossPath(#"/home/rhombus/shape.txt", #'unix)

> p.convention()

#'unix

method

method Path.add(path :: PathString || CrossPath || Path.Dot,

                part :: PathString || CrossPath || Path.Dot,

                ...)

  :: Path.like(path)

 

operator

operator ((path :: PathString || CrossPath || Path.Dot)

            +/ (part :: PathString || CrossPath || Path.Dot))

  :: Path.like(path)

Creates a path given a base path and any number of sub-path extensions (in the case of Path.add) or on extension (in the case of +/). If path is an absolute path, the result is an absolute path, otherwise the result is a relative path.

The path and each part must be either a relative path, the symbol #'up (indicating the relative parent directory), or the symbol #'same (indicating the relative current directory). For Windows paths, if path is a drive specification (with or without a trailing slash) the first part can be a drive-relative path. For all platforms, the last part can be a filename.

The path and part arguments can be paths or cross-platform paths. The platform for the resulting path is inferred from the path and part arguments, where string arguments imply a path for the current platform. If different arguments are for different platforms, the Exn.Fail.Annot exception is thrown. If no argument implies a platform (i.e., all are #'up or #'same), the generated path is for the current platform.

Each part and path can optionally end in a directory separator. If the last part ends in a separator, it is included in the resulting path.

The Path.add procedure builds a path without checking the validity of the path or accessing the filesystem.

> def p = Path("/home/rhombus")

> Path.add(p, "shape.txt")

Path("/home/rhombus/shape.txt")

> p.add("shape.txt")

Path("/home/rhombus/shape.txt")

> p +/ "shape.txt"

Path("/home/rhombus/shape.txt")

Returns a list of path elements that constitute path. Directory-navigation elements are detected and represented as Path.Dot elements. When path is a PathString, then result list’s CrossPath.Element values are more specifically Path.Element values.

The Path.split function computes its result in time proportional to the length of path.

> def p = Path("/home/rhombus/shape.txt")

> Path.split(p)

[Path("/"), Path("home"), Path("rhombus"), Path("shape.txt")]

> p.split()

[Path("/"), Path("home"), Path("rhombus"), Path("shape.txt")]

The Path.name and Path.parent functions produce the last element or path and the path before its last element, respectively.

The Path.directory_only function returns path without its final path element in the case that path is not syntactically a directory; if path has only a single, non-directory path element, #f is returned. If path is syntactically a directory, then path is returned unchanged (but as a path, if it was a string).

The Path.to_directory_path function converts path to one that syntactically represents a directory path if it does not already, typically by adding a path separator to the end.

> def p = Path("/home/rhombus/shape.txt")

> Path.name(p)

Path("shape.txt")

> Path.parent(p)

Path("/home/rhombus/")

> Path.directory_only(p)

Path("/home/rhombus/")

> Path.directory_only(Path.parent(p))

Path("/home/rhombus/")

> Path.to_directory_path(p)

Path("/home/rhombus/shape.txt/")

method

method Path.to_absolute_path(

  path :: PathString || CrossPath,

  ~relative_to: base :: ((PathString.to_path && Path.Absolute)

                           || CrossPath.Absolute)

                  = Path.current_directory()

) :: Path.like(path)

Returns path as an absolute path. If path is a Path or CrossPath and already an absolute path, it is returned as the result. Otherwise, path is resolved with respect to the absolute path base. If base is not an absolute path, the Exn.Fail.Annot exception is thrown. If path and base have different path conventions, then the Exn.Fail.Annot exception is thrown.

> def p = CrossPath(#"shape.txt", #'unix)

> p.to_absolute_path(

    ~relative_to: CrossPath(#"/home/rhombus", #'unix)

  ).bytes()

#"/home/rhombus/shape.txt"

method

method Path.suffix(path :: PathString || CrossPath) :: maybe(Bytes)

 

method

method Path.replace_suffix(

  path :: PathString || CrossPath,

  suffix :: Bytes || ReadableString

) :: Path.like(path)

 

method

method Path.add_suffix(

  path :: PathString || CrossPath,

  suffix :: Bytes || ReadableString,

  ~sep: sep :: Bytes || ReadableString = "_"

) :: Path.like(path)

Extracts or adjusts the suffix of a path, which is the part of Path.name(path).bytes() that starts with with last Byte#"." and runs to the end of the byte string. If the path name has no Byte#".", then the path has no suffix, and Path.suffix returns #false.

The Path.replace_suffix function removes the suffix from a path, if any, and then adds suffix, which can be "" or #"" to just remove a suffix from path or otherwise normally starts with Char"." or Byte#".".

The Path.add_suffix function first changes a Byte#"." in the path that starts its current prefix with sep, and then it adds the given suffix.

> def p = Path("/home/rhombus/shape.txt")

> p.suffix()

#".txt"

> p.replace_suffix(".rhm")

Path("/home/rhombus/shape.rhm")

> p.add_suffix(".rhm")

Path("/home/rhombus/shape_txt.rhm")

The Path.cleanse function removes redundant directory separators, normalizes the separator character for Windows paths, and performs some other normalization steps for unusual Windows paths.

The Path.simplify function performs the same cleansing, and then removes #'same path elements and syntactically resolves #'up elements where preceding elements can be removed. This simplification is purely syntactic and does not consult the filesystem; see also filesystem.simplify_path.

The Path.normal_case function has no effect on platforms that use Unix path conventions, and it case-folds a path’s string form on Windows.

> Path.cleanse("a/..//b")

Path("a/../b")

> Path.simplify("a/..//b")

Path("b")

method

method Path.as_relative_to(

  path :: PathString || CrossPath,

  rel_to_path :: PathString || CrossPath,

  ~more_than_root: more_than_root = #false,

  ~more_than_same: more_than_same = #true,

  ~normal_case: normal_case = #true

) :: Path.like(Path)

Returns a path that is syntactically equivalent to path, but made relative to rel_to_path. Note that constructing a relative path may involve using #'up path elements. For Windows paths, it is possible for no relative path to exist from rel_to_path to path if the paths start with different drives. Typically, path and rel_to_path should be first converted to absolute paths; Path.as_relative_to does not perform that conversion, so it cannot change a relative path given an absolute rel_to_path.

If more_than_root is #true, then if path and rel_to_path has only a root path element in common, then path is returned unchanged (except converted to a Path object if it is a string).

If more_than_same is #true, then if path and rel_to_path are syntactically equivalent, then path is returned unchanged.

If normal is #true, then on Windows, path elements are normalized for comparison. Otherwise, path elements are considered equivalent only when they have the same case.

If path and rel_to_path use different path conventions, the Exn.Fail.Annot exception is thrown.

> def p = Path("/home/rhombus/shape.txt")

> p.as_relative_to("/home")

Path("rhombus/shape.txt")

> p.as_relative_to("/home/racket")

Path("../rhombus/shape.txt")

14.1.1 Runtime Paths🔗ℹ

 import: rhombus/runtime_path package: rhombus-lib

definition

runtime_path.def id:

  body

  ...

Defines id to provide an absolute path that refers to the file name produced by the body sequence as interpreted relative to the source module.

An unusual property of runtime_path.def is that the body sequence is used in both a run-time context and a meta context, so it must be valid for both. The meta interpretation is used for tasks like creating a standalone executable to ensure that referenced files are accessible at run time.

> import:

    rhombus/meta open

    rhombus/runtime_path

> runtime_path.def image_file: "file.png"