43 个版本 (21 个稳定版本)
1.9.3 | 2024 年 5 月 3 日 |
---|---|
1.9.2 | 2023 年 12 月 26 日 |
1.9.0 | 2023 年 8 月 17 日 |
1.8.0 | 2023 年 3 月 5 日 |
0.1.5 | 2017 年 11 月 5 日 |
#7 in 文件系统
1,314,907 每月下载量
用于 1,083 个 Crates (91 个直接使用)
115KB
2K SLoC
relative-path
为 Rust 提供便携式相对 UTF-8 路径。
此 crate 提供一个类似于 std::path
的模块,具有以下特性
- 路径分隔符设置为固定字符(
/
),无论平台如何。 - 相对路径不能表示文件系统中的路径,除非首先使用诸如
to_path
和to_logical_path
的函数指定它们是相对于什么的。 - 相对路径始终保证是有效的 UTF-8 字符串。
在此基础上,我们还支持许多确保跨平台一致行为的操作。
有关更多操作相对路径的实用程序,请参阅 relative-path-utils
crate。
用法
将 relative-path
添加到您的 Cargo.toml
relative-path = "1.9.2"
开始使用相对路径
use serde::{Serialize, Deserialize};
use relative_path::RelativePath;
#[derive(Serialize, Deserialize)]
struct Manifest<'a> {
#[serde(borrow)]
source: &'a RelativePath,
}
Serde 支持
此库包括 serde 支持,可以使用 serde
功能启用。
为什么 std::path
是一个便携性风险?
路径表示在不同平台间不同。
- Windows 允许使用驱动器卷(多个根)作为前缀(例如
"c:\"
)和反斜杠(\
)作为分隔符。 - Unix 从单个根引用绝对路径,并使用正斜杠(
/
)作为分隔符。
如果我们使用 PathBuf
,在清单中存储路径将允许我们的应用程序在一个平台上构建和运行,但在其他平台上可能不行。
考虑以下数据模型和相应的清单 toml
use std::path::PathBuf;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Manifest {
source: PathBuf,
}
source = "C:\\Users\\udoprog\\repo\\data\\source"
这将为您运行(假设 source
存在)。因此,您继续将清单检入 git。第二天,您的 Linux 同事给您打电话,想知道他们什么时候得罪了您?
出了什么问题?好吧,两件事。您忘记将 source
设置为相对路径,所以公司里任何用户名与您不同的人都无法使用它。所以您继续修复它。
source = "data\\source"
但还有一个问题!反斜杠(\
)在 Windows 上才是一个合法的路径分隔符。幸运的是,您了解到正斜杠在 Windows 和 Linux 上都受支持。所以您选择了
source = "data/source"
现在一切正常。所以一切顺利... 对吧?当然,但我们可以做得更好。
这个包提供与 可移植相对路径 一起工作的类型(因此得名)。因此,通过使用 RelativePath
,我们可以系统地帮助避免上述之类的可移植性问题。在源头避免问题比花 5 分钟时间在理论上解决问题要好,希望新员工在遇到这种情况时能记住该怎么做。
使用 RelativePathBuf
,我们可以这样修复我们的数据模型
use relative_path::RelativePathBuf;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct Manifest {
source: RelativePathBuf,
}
以及它的使用位置
use std::fs;
use std::env::current_dir;
let manifest: Manifest = todo!();
let root = current_dir()?;
let source = manifest.source.to_path(&root);
let content = fs::read(&source)?;
概述
将转换为特定平台的 Path
通过 to_path
和 to_logical_path
函数进行。您需要指定相对路径的前缀路径。这可以来自像 std::env::current_dir
这样的函数。
use std::env::current_dir;
use std::path::Path;
use relative_path::RelativePath;
let root = current_dir()?;
// to_path unconditionally concatenates a relative path with its base:
let relative_path = RelativePath::new("../foo/./bar");
let full_path = relative_path.to_path(&root);
assert_eq!(full_path, root.join("..\\foo\\.\\bar"));
// to_logical_path tries to apply the logical operations that the relative
// path corresponds to:
let relative_path = RelativePath::new("../foo/./bar");
let full_path = relative_path.to_logical_path(&root);
// Replicate the operation performed by `to_logical_path`.
let mut parent = root.clone();
parent.pop();
assert_eq!(full_path, parent.join("foo\\bar"));
当两个相对路径相互比较时,它们的精确组件组成决定了相等性。
use relative_path::RelativePath;
assert_ne!(
RelativePath::new("foo/bar/../baz"),
RelativePath::new("foo/baz")
);
不支持使用特定平台的路径分隔符来构建相对路径。
来自其他平台的路径分隔符简单地被视为组件的一部分
use relative_path::RelativePath;
assert_ne!(
RelativePath::new("foo/bar"),
RelativePath::new("foo\\bar")
);
assert_eq!(1, RelativePath::new("foo\\bar").components().count());
assert_eq!(2, RelativePath::new("foo/bar").components().count());
要检查两个相对路径是否等效,您可以使用 normalize
use relative_path::RelativePath;
assert_eq!(
RelativePath::new("foo/bar/../baz").normalize(),
RelativePath::new("foo/baz").normalize(),
);
其他可移植性注意事项
虽然相对路径避免了最严重的可移植性问题,即绝对路径将在所有平台上都同样不工作。我们无法避免所有。本节试图记录我们了解的其他可移植性风险。
RelativePath
,与 Path
类似,不保证其组成部分构成合法的文件名。虽然组件由斜杠严格分隔,但我们仍然可以存储在其中,这些内容可能在所有平台上都不能作为合法路径使用。
- Unix 平台上不允许使用
NUL
字符——这是基于 C 的文件系统 API 中的终止符。斜杠(/
)也用作路径分隔符。 - Windows 有许多保留字符和名称(如
CON
、PRN
和AUX
),它们不能合法地成为文件系统组件的一部分。 - Windows 路径默认是不区分大小写的。因此,
Foo.txt
和foo.txt
在 Windows 上是相同的文件。但在大多数 Unix 系统上,它们被认为是不同的路径。
一个意外包含特定平台组件的相对路径将很可能生成无意义的路径,希望在开发和测试期间快速失败。
use relative_path::{RelativePath, PathExt};
use std::path::Path;
if cfg!(windows) {
assert_eq!(
Path::new("foo\\c:\\bar\\baz"),
RelativePath::new("c:\\bar\\baz").to_path("foo")
);
}
if cfg!(unix) {
assert_eq!(
Path::new("foo/bar/baz"),
RelativePath::new("/bar/baz").to_path("foo")
);
}
assert_eq!(
Path::new("foo").relative_to("bar")?,
RelativePath::new("../foo"),
);
依赖关系
~165KB