#path #dsl #utility

path-dsl

一个用于处理路径和PathBuf的实用DSL和宏

12个版本

0.6.1 2020年7月11日
0.6.0 2020年7月11日
0.5.4 2019年8月25日
0.4.0 2019年8月13日
0.1.1 2019年8月9日

#455 in 开发工具

Download history 16/week @ 2024-03-11 4/week @ 2024-03-18 14/week @ 2024-03-25 361/week @ 2024-04-01 3/week @ 2024-04-08 847/week @ 2024-04-15 109/week @ 2024-04-22 12/week @ 2024-04-29 935/week @ 2024-05-06 69/week @ 2024-05-13 25/week @ 2024-05-20 132/week @ 2024-05-27 559/week @ 2024-06-03 693/week @ 2024-06-10 71/week @ 2024-06-17 73/week @ 2024-06-24

1,397 每月下载量
用于 bellboy

MIT 许可证

55KB
924

path-dsl

Latest version Documentation License

用于处理路径的实用DSL和宏。

PathDSL为创建路径和向现有类似Path的对象追加内容提供了一个简单且无开销的抽象。

概述

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

// PathBuf::push() only called once with consecutive literals:
let literals: PathBuf = path!("dir1" | "dir2" | "dir3");
// Type annotation for illustration purposes; not needed

// Does not copy data if first path segment is a owning value:
let moving = path!(literals | "dir4");

// Mixing and matching is easy:
let start = path!("some" | "dir");
let end = path!("my_folder" | "my_file.txt");
// Can borrow as normal
let result = path!(start | "middle_folder" | &end);

// Works with PathBuf, Path, and String-likes
let file = Path::new("file.txt");
let folder = PathBuf::from("folder");
let middle: &str = "other_middle";
let combined = path!(folder | middle | "middle_folder" | file);

PathDSL宏和类型

PathDSL的path!宏允许在当前情况下以最有效的方式创建一个PathBuf

注意由于Rust的宏规则,使用|代替/

use path_dsl::path;
// Type annotation for illustration only, not needed
let path: PathBuf = path!("dir1" | "dir2" | "dir3" | "file.txt");

PathDSL

虽然不推荐,但你也可以直接生成PathDSL。PathDSL会尽量表现得像PathBuf,但通常使用path!宏直接生成PathBuf会更高效。

use path_dsl::PathDSL;
let path = PathDSL::from("dir1") / "dir2" / "dir3" / "file.txt";

添加路径结构

除了使用常规字符串字面量外,你还可以使用任何可以作为DSL的一部分传递给PathBuf::push的值。

注意对other的借用:由于这些类型不是Copy,除非你进行借用,否则它们将被移动到路径中。这符合与PathBuf::push的行为,但在中缀表达式中可能会令人惊讶。

use path_dsl::{path, PathDSL};

let other = PathBuf::from("some_dir");
let filename: &str = "my_file.txt";

let mac: PathBuf  = path!("dir1" | "dir2" | &other | filename); // Preferred
let path: PathDSL = PathDSL::from("dir1") / "dir2" / other / filename; // Also works

移动与借用

宏和DSL类型在借用与移动方面表现相同。如果提供了引用,它将借用提供的值。然而,如果提供了值,它将移动该值,使其之后不可用。虽然这些是Rust的正常规则,但中缀运算符通常用于Copy类型,所以这可能会令人惊讶。

支持可变和不可变借用,尽管它们永远不会实际修改任何内容。

use path_dsl::path;
# use std::path::PathBuf;

let value = PathBuf::from("some_dir");
let borrow: &str = "my_file.txt";

let mac  = path!(value | borrow);
let path = path!(value | borrow); // Will not compile because `value` was moved

你必须手动进行借用

let mac  = path!(&value | borrow); // Borrow value so it can be used later
let path = PathDSL::new() / value / borrow; // Not used afterwards, so doesn't need a borrow

PathDSL <=> PathBuf

PathDSL 设计为 PathBuf 的即插即用替代品,包括两者之间的简单转换。在任何可以使用 PathBuf 的场合,您都可以使用 PathDSLPathDSL 包含一个指向 PathBuf(通过代理 Path)的 Deref 实现,并重新实现了所有接受 self 的函数,因此与 API 完全兼容。然而,有些情况下您必须使用 PathBuf。通过解引用可以获得 &PathBuf,而通过 PathDSL::into_pathbuf 函数可以获得 PathBuf

PathDSLPathBuf 上是 #[repr(transparent)],并且所有函数都被强制内联,因此与等效的 PathBuf 操作相比,转换和操作应该是免费的。如果不是这样,请提交一个错误报告。

一些已知问题包括

相等性

use path_dsl::path;

let dsl = path!("file.txt");
let buf = PathBuf::from("file.txt");

assert!(dsl == buf);
// Must de-reference to PathBuf can't implement `Eq` for `PathBuf`
assert!(buf == *dsl);

函数调用

use path_dsl::path;

fn func(p: PathBuf) {
}

let dsl = path!("file.txt");
let buf = PathBuf::from("file.txt");

func(buf);
// Must convert into `PathBuf`
// Dereferencing doesn't work because `func` moves.
func(dsl.to_path_buf());
func(dsl.into()) // also works

宏优化

如前所述,该宏包含一些针对使用原始 PathDSL 的优化,并且应该始终使用宏而不是手动使用 PathDSL。这些优化发生在编译时,并得到保证。更多详情可以在 path! 宏文档中找到。

字符串字面量连接

虽然不建议在 Path 中使用带有斜杠的字符串字面量,但 path! 宏会考虑斜杠,并自动从多个连续的字符串字面量中构造一个单一的字符串字面量。这可能在底层的 OsString 中节省一个或两个分配。

use path_dsl::path;

let p = path!("this" | "is" | "combined");
if cfg!(windows) {
    assert_eq!(p, PathBuf::from("this\\is\\combined"));
} else {
    assert_eq!(p, PathBuf::from("this/is/combined"));
}

第一个参数优化

path! 宏的第一个参数是一个拥有 PathBufOsStringPathDSL 的值(移动),而不是将所有内容复制到一个新的 PathDSL 中,它只会从移动进来的值中窃取缓冲区。这允许您在追加到已存在的变量时放心地使用 path! 宏。

use path_dsl::path;

let first = PathBuf::from("a_very_long_folder_name");
let p = path!(first); // Does not copy anything.

为什么要使用一个 crate?

您可能想知道为什么您应该使用一个 crate 来做这件事,而不是简单地包装 PathBuf 并添加一些 Div 实现。这正是我之前所想的,直到我真正开始实现这个 crate。直接尝试模拟 PathBuf 需要编写大量非常繁琐且特定的代码,以及测试功能。

考虑到这一点,我已经将path_dsl完全去除了依赖,选择依赖声明宏而不是过程宏,以避免依赖于像syn这样的东西。此外,所有内容都包含在这个文件中,我进行了彻底的测试,并且为了谨慎起见,我添加了#[deny(unsafe_code)]。希望这能让这个crate足够轻量并且易于审计,成为一个可接受的依赖。

许可证:MIT

无运行时依赖