#error-context #error #error-derive #derive #macro-derive #io-error

thisctx

轻松创建带有上下文错误的错误

4个版本 (重大更改)

0.4.0 2023年3月20日
0.3.0 2023年3月11日
0.2.0 2023年1月9日
0.1.0 2021年6月18日

#739Rust模式

Download history 39/week @ 2024-03-16 33/week @ 2024-03-23 41/week @ 2024-03-30 121/week @ 2024-04-06 29/week @ 2024-04-13 39/week @ 2024-04-20 30/week @ 2024-04-27 36/week @ 2024-05-04 22/week @ 2024-05-11 29/week @ 2024-05-18 30/week @ 2024-05-25 24/week @ 2024-06-01 26/week @ 2024-06-08 35/week @ 2024-06-15 17/week @ 2024-06-22 6/week @ 2024-06-29

每月下载量85

MIT/Apache

28KB
178

🎈 thisctx

一个小型的crate,与thiserror一起工作,用于创建带有上下文的错误,深受snafu的启发。

✍️ 示例

#[derive(Debug, Error, WithContext)]
pub enum Error {
    #[error("I/O failed at '{1}'")]
    Io(#[source] std::io::Error, PathBuf),
    #[error(transparent)]
    ParseInt(std::num::ParseIntError),
}

fn read_file(path: &Path) -> Result<String, Error> {
    std::fs::read_to_string(path).context(Io(path))
}

⚙️ 属性

您可以使用以下选项使用#[thisctx]属性来自定义展开的代码

选项 类型 继承 容器 变体 字段
attr TokenStream[]
generic bool
into 类型[]
module bool |Ident
skip Ident
suffix bool |Ident
unit bool
visibility 可见性

#[source]#[error]中定义的属性也会被检查,以确定源错误类型。

选项参数

#[thisctx(支持传递参数到选项的两种语法

  • 直接将标记放入括号内,例如:#[thisctx(visibility(pub))]
  • 使用字符串字面量,例如:#[thisctx(visibility = "pub")],这在不支持在非宏属性中使用任意标记的旧版本rustc中很有用。

类型为 T[] 的选项可以在同一节点中出现多次,而其他类型的选项会导致错误。

布尔选项

布尔选项中可以省略 true 的值,例如 #[thisctx(skip)] 等于 #[thisctx(skip(true))]

no_ 开头的反向布尔选项也可以用作传递 false 的快捷方式,例如 #[thisctx(no_skip)] 等于 #[thisctx(skip(false))]

继承选项

如果未提供值,继承选项将使用其父节点的值,例如

#[derive(WithContext)]
#[thisctx(skip)]
enum Error {
    // This variant will be ignored since `skip=true` is inherited.
    Io(#[source] std::io::Error),
    // This variant will be processed.
    #[thisctx(no_skip)]
    ParseInt(#[source] std::num::ParseIntError),
}

类型为 T[] 的选项将连接祖先节点中的参数,而不是覆盖它们。

#[derive(WithContext)]
#[thisctx(attr(derive(Debug)))]
enum Error {
    #[thisctx(attr(derive(Clone, Copy)))]
    Io(#[source] std::io::Error),
    ParseInt(#[source] std::num::ParseIntError),
}

扩展示例

// The order of attributes (and other options) is guaranteed by the order of
// inheritance.
// Attributes from the child node.
#[derive(Clone, Copy)]
// Attributes from the parent node.
#[derive(Debug)]
struct Io;

#[derive(Debug)]
struct ParseInt;

源代码

如果一个字段有 #[source] 属性或被命名为 source,则该字段的类型将被分配为 IntoError::Source,并且不会出现在生成的上下文类型中。

#[derive(WithContext)]
struct Error(#[source] std::io::Error, PathBuf);

扩展示例

struct ErrorContext<T1 = PathBuf>(T1);

impl<T1> IntoError<Error> for ErrorContext<T1>
where
    T1: Into<PathBuf>,
{
    type Source = std::io::Error;

    fn into_error(self, source: Self::Source) -> Error {
        Error(source, self.0.into())
    }
}

错误

如果变体是透明的(具有 #[error(transparent)]),则第一个字段(应该是唯一字段)将被视为源字段。

thisctx.attr

用于向生成的节点添加额外属性的选项。

#[derive(WithContext)]
#[thisctx(attr(derive(Debug)))]
struct Error {
    reason: String,
}

扩展示例

#[derive(Debug)]
struct ErrorContext<T1 = String> {
    reason: T1,
}

thisctx 允许您在不使用 attr(...) 的情况下添加一些常见属性,包括

  • cfg
  • cfg_attr
  • derive
  • doc

这意味着上面的示例也可以写成

#[derive(WithContext)]
#[thisctx(derive(Debug))]
struct Error {
    reason: String,
}

thisctx.generic

用于禁用生成节点泛型的选项。

#[derive(WithContext)]
struct Error {
    reason: String,
    #[thisctx(no_generic)]
    path: PathBuf,
}

扩展示例

struct ErrorContext<T1 = String> {
    reason: T1,
    path: PathBuf,
}

泛型提供了一种方便的方式来构造上下文类型,例如

let _: Error = ErrorContext {
    // You can use &str directly because String implements From<&str>,
    reason: "anyhow",
    // whereas without generics you have to convert the data to PathBuf manually.
    path: "/some/path".into(),
}.build();

thisctx.into

用于将生成的类型转换为远程错误类型的选项。

// Probably an error defined in another crate.
enum RemoteError {
    Custom(String),
}

// From<T> is required by #[thisctx(into)]
impl From<MyError> for RemoteError {
    fn from(e: MyError) -> Self {
        Self::Custom(e.0)
    }
}

#[derive(WithContext)]
#[thisctx(into(RemoteError))]
struct MyError(String);

let _: MyError = MyErrorContext("anyhow").build();
// It's possible to construct a remote error from the local context type.
let _: RemoteError = MyErrorContext("anyhow").build();

thisctx.module

此选项允许您将所有生成的上下文类型放入单个模块中。

#[derive(WithContext)]
#[thisctx(module(context))]
pub enum Error {
    Io(#[source] std::io::Error),
    ParseInt(#[source] std::num::ParseIntError),
}

扩展示例

pub mod context {
    pub struct Io;
    pub struct ParseInt;
}

您还可以将此选项设置为 true 来使用容器名称的蛇形命名作为模块名称,例如在 #[thisctx(module)]enum MyError 等于 #[thisctx(module(my_error))]

thisctx.skip

此选项用于跳过为指定变体生成上下文类型。

#[derive(WithContext)]
enum Error {
    #[thisctx(skip)]
    Io(#[source] std::io::Error),
    ParseInt(#[source] std::num::ParseIntError),
}

扩展示例

struct ParseInt;

thisctx.suffix

一个选项,用于在生成的上下文类型的名称后添加后缀。

默认情况下,只有 struct 将添加内建后缀 Context,因为不带后缀生成的类型将与错误类型冲突。

#[derive(WithContext)]
#[thisctx(suffix(Error))]
enum Error {
    Io(#[source] std::io::Error),
    ParseInt(#[source] std::num::ParseIntError),
}

扩展示例

struct IoError;
struct ParseIntError;

true 表示使用默认后缀 Context,而值 false 将从生成的类型中删除后缀。

thisctx.unit

在 Rust 中,即使它为空,也必须使用括号来构造元组结构体。默认情况下,thisctx 会将空结构体转换为单元结构体。这允许您使用结构体名称创建新的上下文,而无需每次都添加括号,可以通过传递 #[thisctx(no_unit)] 来禁用此功能。

#[derive(WithContext)]
enum Error {
    #[thisctx(no_unit)]
    Io(#[source] std::io::Error),
    ParseInt(#[source] std::num::ParseIntError),
}

扩展示例

struct IoError();
struct ParseIntError;

thisctx.visibility

此选项用于更改生成的类型和字段的可见性,可以简写为 #[pub(...)]

#[derive(WithContext)]
#[thisctx(pub(crate))]
pub enum Error {
    Io(#[source] std::io::Error),
    ParseInt(#[source] std::num::ParseIntError),
}

扩展示例

pub(crate) struct IoError;
pub(crate) struct ParseIntError;

📝 待办事项

  • 切换到 Rust 2021。
  • MSRV v1.33
  • 使用 derive 宏代替。
  • 向上下文类型添加属性。
  • 支持透明错误。
  • 支持泛型。
  • 简化 derive 实现。
  • 更多文档。
  • 更多测试。

🚩 最小支持的 Rust 版本

所有测试在 tests/* 下通过 rustc v1.33 运行,旧版本可能无法编译。

⚖️ 许可证

根据您的选择,许可如下:

依赖项

~1.5MB
~35K SLoC