5 个版本

0.1.4 2023 年 6 月 3 日
0.1.3 2023 年 4 月 27 日
0.1.2 2023 年 4 月 15 日
0.1.1 2023 年 4 月 8 日
0.1.0 2023 年 4 月 8 日

#8#error-context

Download history 92/week @ 2024-03-11 47/week @ 2024-03-18 36/week @ 2024-03-25 71/week @ 2024-04-01 38/week @ 2024-04-08 69/week @ 2024-04-15 45/week @ 2024-04-22 25/week @ 2024-04-29 55/week @ 2024-05-06 41/week @ 2024-05-13 28/week @ 2024-05-20 14/week @ 2024-05-27 28/week @ 2024-06-03 46/week @ 2024-06-10 24/week @ 2024-06-17 14/week @ 2024-06-24

113 每月下载量
用于 4 个包 (2 直接)

MIT 许可证

44KB
1K SLoC

anyhow-std

此包封装了某些 [std] 功能以生成提供更好错误上下文的 anyhow::Result

示例:期望一个有效的 UTF8 路径扩展

假设我们正在处理用户提供的路径,并 期望 它具有一个有效的 UTF8 扩展

use std::path::Path;
use anyhow_std::{PathAnyhow, OsStrAnyhow};

fn process_user_path(path: &Path) -> anyhow::Result<()> {
    let extos = path.extension_anyhow()?;
    let ext = extos.to_str_anyhow()?;
    process_user_path_with_extension(path, ext)?;
    Ok(())
}

fn process_user_path_with_extension(path: &Path, extension: &str) -> anyhow::Result<()> {
    todo!("implement path processing of {path:?} for the extension {extension:?}")
}

/*
Now if the user provides a path without an extension, they'll get an
error message with more helpful context:
*/

let res = process_user_path(Path::new("/tmp/no-extension"));
assert!(res.is_err());

let error_message = format!("{:#}", res.err().unwrap());
assert_eq!(
    error_message,
    r#"while processing path "/tmp/no-extension": missing expected extension"#
);

/*
Unix systems can have non-UTF8 paths:
*/

#[cfg(target_family = "unix")]
fn demonstrate_non_utf8_extension() {
    use std::ffi::OsStr;
    use std::os::unix::ffi::OsStrExt;

    let res = process_user_path(Path::new(OsStr::from_bytes(b"/tmp/non-unicode-extension.wacky-\xf3-extension")));
    assert!(res.is_err());

    let error_message = format!("{:#}", res.err().unwrap());
    assert_eq!(
        error_message,
        r#"while processing os string "wacky-�-extension": not valid utf8"#,
    );
}

#[cfg(target_family = "unix")]
demonstrate_non_utf8_extension();

扩展特质

使用一种常见的模式来扩展 [std] 类型

  • 提供了一个名为 <std type>Anyhow 的扩展特质,例如:[OsStrAnyhow]
  • 提供了针对目标类型的 impl,例如 impl OsStrAnyhow for OsStr {}
  • 为目标类型的某些方法提供了扩展特质方法,方法名为 <method name>_anyhow。这些方法始终返回 anyhow::Result 类型,例如:OsStrAnyhow::to_str_anyhow
  • 此外,还提供了一些 …_anyhow 方法来封装在目标类型上找不到的功能。例如,Path::read_to_string 不存在,但可以作为一个简单的包装器,因此这个包提供了 PathAnyhow::read_to_string_anyhow

通过始终将 _anyhow 添加到包装方法中,调用者可以明确选择何时使用这些方法与 [std] 方法。

…_anyhow 方法

这些方法将[std]方法返回的类型Option<T>Result<T, E>转换为anyhow::Result<T>,其中anyhow::Error添加了针对[std]类型的特定上下文。

对于返回Option<T>的[std]方法,通常返回类型为None并不一定是“错误”,但是在这种情况下,…_anyhow方法会导致错误。因此,这些方法只能在代码期望Some结果时使用,如果您的代码应该将None视为“非错误”,则可以简单地使用[std]方法。

包装类型

在某些情况下,需要使用“包装类型”模式而不是扩展特质,主要是为了跟踪错误上下文中使用的额外数据。例如,crate::fs包装类型ReadDirDirEntryMetadata各自除了拥有底层std::fs类型外,还拥有PathBuf,以便在错误上下文中提供路径,以便为常见的目录和文件系统遍历使用提供有用的错误上下文。

包装类型覆盖了一些底层[std]类型的方法,而不是使用…_anyhow命名约定。它们还提供了一个std::ops::Deref实现,以便对底层[std]类型进行操作,因此可以自然地调用所有未覆盖的方法,并通过显式使用wrapper.deref().target_method()来调用覆盖的方法。

使用这种包装类型模式的类型始终在可能的情况下为底层[std]类型以及任何错误上下文数据提供[From] / [Into]实现。例如,对于crate::fs::Metadata,提供了[From] / [Into]实现,为(std::fs::Metadata, std::fs::PathBuf),后者提供错误上下文。对于Child有一个例外,因为它在构造时修改了底层的std::process::Child

最后,Output类型是这种包装模式的一个例外,因为它不提供任何方法,并将所有内容公开为pub字段。

API 覆盖范围

这个crate只包装了作者在其他项目中需要的基于[std]的小子集。如果您想看到更多[std] API被包装,欢迎提交补丁。;-)

0.1.x》版本系列将根据有用性添加API,并可能更改错误上下文字符串。封装函数的语义不应有很大变化,但可能会。

依赖项

~0.4–0.9MB
~20K SLoC