#path #relative #utf-8 #platform #deserialize #serialization

relative-path

为 Rust 提供便携式相对路径

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 文件系统

Download history 197844/week @ 2024-05-02 188384/week @ 2024-05-09 212860/week @ 2024-05-16 219791/week @ 2024-05-23 270373/week @ 2024-05-30 287697/week @ 2024-06-06 291186/week @ 2024-06-13 315645/week @ 2024-06-20 307976/week @ 2024-06-27 284250/week @ 2024-07-04 300215/week @ 2024-07-11 299137/week @ 2024-07-18 310294/week @ 2024-07-25 314454/week @ 2024-08-01 311603/week @ 2024-08-08 320171/week @ 2024-08-15

1,314,907 每月下载量
用于 1,083 个 Crates (91 个直接使用)

MIT/Apache

115KB
2K SLoC

relative-path

github crates.io docs.rs build status

为 Rust 提供便携式相对 UTF-8 路径。

此 crate 提供一个类似于 std::path 的模块,具有以下特性

  • 路径分隔符设置为固定字符(/),无论平台如何。
  • 相对路径不能表示文件系统中的路径,除非首先使用诸如 to_pathto_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_pathto_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 类似,不保证其组成部分构成合法的文件名。虽然组件由斜杠严格分隔,但我们仍然可以存储在其中,这些内容可能在所有平台上都不能作为合法路径使用。

一个意外包含特定平台组件的相对路径将很可能生成无意义的路径,希望在开发和测试期间快速失败。

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