Skip to main content

path

Macro path 

Source
macro_rules! path {
    ($(&)? ident / $(ident_or_literal_or_expr) / * ) => { ... };
    (literal / $(ident_or_literal_or_expr) / * ) => { ... };
    ($(&)? ( path_expression ) / $(ident_or_literal_or_expr) / *) => { ... };
}
Expand description

Efficient Path-join macro

The macro rules above is only for illustration purpose, see source code for implementation

§Usage

The macro efficiently creates joined paths from either a owned PathBuf or a borrowed Path reference (impl AsRef<Path>), and one or more path segments reference to join. The OS separator is used (i.e. \ on Windows).

The format of the macro in pseudocode is:

cu::path!( FIRST_SEG  $( / NEXT_SEG )* )

FIRST_SEG can be:

  • A owned PathBuf ident
    • e.g. cu::path!(my_path_buf / ...)
  • A borrowed &Path ident:
    • e.g. cu::path!(&my_path / ...)
    • Here & is the macro rule to indicate you don’t want to borrow the path, so you need it even when my_path is already a borrowed path
  • A literal string, which you can use without &
    • e.g. cu::path!("my_path" / ...)
  • An expression that evaluates to a owned PathBuf
    • e.g. cu::path!( (get_path()) / ...)
    • Expression needs to be parenthesized because / cannot follow an expression in macro rules. { } also works
  • An expression that evaludates to a borrowed &Path
    • e.g. cu::path!( &(my.path) / ... )
    • Expression needs to be parenthesized because / cannot follow an expression in macro rules. { } also works
    • Here & is the macro rule to indicate you don’t want to borrow the path, so you need it even when my_path is already a borrowed path

Each NEXT_SEG can be:

  • A literal string
  • An ident (the macro will not take ownership of the variable)
  • An expression wrapped with either ( ) or { }. The last expression doesn’t need to be wrapped

§Examples

use std::path::{Path, PathBuf};

// From a literal string
let p1 = cu::path!("home" / "user");
let p2 = cu::path!("home" / "user" / "docs");
assert_eq!(p1, PathBuf::from("home").join("user"));
assert_eq!(p2, PathBuf::from("home").join("user").join("docs"));

// From an owned PathBuf ident (base is moved)
let base = PathBuf::from("usr").join("local");
let p = cu::path!(base / "bin" / "tool");
assert_eq!(p, PathBuf::from("usr").join("local").join("bin").join("tool"));

// From a borrowed &Path ident (use `&` even if already a reference)
let base = PathBuf::from("etc");
let base_ref: &Path = base.as_path();
let p = cu::path!(&base_ref / "nginx" / "nginx.conf");
assert_eq!(p, PathBuf::from("etc").join("nginx").join("nginx.conf"));

// From an expression returning PathBuf (must be parenthesized)
let p = cu::path!((PathBuf::from("usr").join("local")) / "bin");
assert_eq!(p, PathBuf::from("usr").join("local").join("bin"));

// From an expression returning &Path (must be parenthesized, and needs `&`)
let owned = PathBuf::from("var");
let p = cu::path!(&(owned.as_path()) / "log");
assert_eq!(p, PathBuf::from("var").join("log"));

// NEXT_SEG can be an ident — not moved, still usable after
let dir = "subdir";
let file = "file.txt";
let p = cu::path!("root" / dir / file);
assert_eq!(p, PathBuf::from("root").join(dir).join(file));
let _ = (dir, file); // still accessible

// NEXT_SEG can be an expression (must be parenthesized)
let sub = String::from("sub");
let p = cu::path!("root" / (sub.as_str()) / "output.log");
assert_eq!(p, PathBuf::from("root").join("sub").join("output.log"));

§Implementation

Currently this uses the same implementation as the standard library (as of 1.95.0) that does not do any probing to pre-allocate the path based on the input iterator. Each segment is .push()-ed onto the initial buffer in a loop.