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日 |
#739 在 Rust模式
每月下载量85
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
运行,旧版本可能无法编译。
⚖️ 许可证
根据您的选择,许可如下:
- Apache License,版本 2.0 (LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT 许可证 (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
。
依赖项
~1.5MB
~35K SLoC