2 个版本
0.1.1 | 2024 年 2 月 11 日 |
---|---|
0.1.0 | 2024 年 2 月 11 日 |
#1791 in Rust 模式
38KB
639 行
try_convert
自动生成 TryFrom 和错误类型,以最小化模板代码。
用法
将此添加到您的 Cargo.toml
[dependencies]
try_convert = "0.1"
thiserror = "1.0" # Optional, but recommended
功能
thiserror
(默认启用): 使用thiserror
为错误类型派生Error
。
容器属性
#[derive(TryConvert)]
: 为结构体或枚举派生TryFrom
。#[try_convert(from = "source::Struct")]
: 为注解类型派生TryFrom<source::Struct>
。#[try_convert(from = "source::Enum", exclude("D { .. }", "E ( .. )"))]
: 为注解类型派生TryFrom<source::Enum>
,排除模式D { .. }
和E (..)
。#[try_convert(from = "source::Enum", error = "MyEnumError")]
:为注释类型推导出TryFrom<source::Enum>
,使用MyEnumError
作为错误类型。#[try_convert(from = "source::Struct", error = "MyEnumError", description = "the struct is invalid")]
:为注释类型推导出TryFrom<source::Struct>
,使用MyEnumError
作为错误类型,描述为 "the struct is invalid"。
当未指定 error
时,错误类型将被生成为 AnnotatedTypeFromSourceTypeError
。
字段属性
#[try_convert(from = "i32")]
:使用TryFrom<i32>
转换注释字段。您还可以指定error
和description
以覆盖默认的错误变体和消息。如果未指定error
,错误变体将为<field>To<type>
。#[try_convert(unwrap, from = "Option<i32>")]
:尝试展开源字段,如果其为None
则生成错误。您还可以指定error
和description
以覆盖默认的错误变体和消息。如果未指定error
,错误变体将为SomeFieldIsNone
。#[try_convert(map, from = "Vec<i32>")]
:将源Vec
的每个元素映射到目标类型,使用TryFrom
。您还可以指定error
和description
来覆盖默认的错误变体和消息。#[try_convert(filter = "|x| x.is_empty()", error = "EmptyString", description = "字符串为空")]
:过滤源字段,如果过滤器返回false
则生成错误。需要指定error
。#[try_convert(get = "from.some_string")]
:从源字段some_string
获取值。在所有其他操作之前执行。只能指定一次。
可以将这些属性链在一起以对单个字段执行多个操作。
变体属性
#[try_convert(from = "source::Enum::A(f0)")]
:当源为source::Enum::A(f0)
时,将其转换为注解变体。
示例
以下代码
mod source {
pub struct Struct {
pub some_passthrough: usize,
pub some_string: String,
pub some_option: Option<i32>,
pub some_vec: Option<Vec<i32>>,
pub some_enum: Enum,
}
pub(crate) enum Enum {
A(String),
B { a: i32, b: i32 },
C,
D { c: i32 },
E,
}
}
use try_convert::TryConvert;
#[derive(TryConvert)]
#[try_convert(from = "source::Struct")]
pub struct MyStruct {
some_passthrough: usize,
#[try_convert(unwrap, from = "Option<i32>")]
some_option: i32,
#[try_convert(unwrap, from = "Option<Vec<i32>>")]
#[try_convert(map, from = "Vec<i32>", error = "CustomErrorVariant")]
some_vec: Vec<i16>,
#[try_convert(from = "source::Enum")]
some_enum: MyEnum,
#[try_convert(get = "from.some_string")]
#[try_convert(
filter = "|x| !x.is_empty()",
error = "EmptyString",
description = "the string is empty"
)]
some_renamed_string: String,
}
#[derive(TryConvert)]
#[try_convert(from = "source::Enum", exclude("D { .. }", "E"), error = "MyEnumError")]
pub(crate) enum MyEnum {
#[try_convert(from = "source::Enum::A(f0)")]
A(String),
#[try_convert(from = "source::Enum::B { a, b }")]
B {
#[try_convert(from = "i32")]
a: i8,
#[try_convert(get = "b")]
#[try_convert(from = "i32")]
renamed: i16,
},
C,
}
将扩展为
#[derive(Debug, thiserror::Error)]
pub enum MyStructFromSourceStructError {
#[error("some option is none")]
SomeOptionIsNone,
#[error("some vec is none")]
SomeVecIsNone,
#[error("custom error variant: {0:?}")]
CustomErrorVariant(<i16 as TryFrom<i32>>::Error),
#[error("some enum to my enum: {0:?}")]
SomeEnumToMyEnum(<MyEnum as TryFrom<source::Enum>>::Error),
#[error("the string is empty")]
EmptyString,
}
impl TryFrom<source::Struct> for MyStruct {
type Error = MyStructFromSourceStructError;
fn try_from(from: source::Struct) -> Result<Self, Self::Error> {
Ok({
let some_passthrough = from.some_passthrough.into();
let some_option = from
.some_option
.ok_or(MyStructFromSourceStructError::SomeOptionIsNone)?
.into();
let some_vec = from
.some_vec
.ok_or(MyStructFromSourceStructError::SomeVecIsNone)?
.into_iter()
.map(TryFrom::try_from)
.collect::<Result<Vec<i16>, _>>()
.map_err(MyStructFromSourceStructError::CustomErrorVariant)?
.into();
let some_enum = <MyEnum as TryFrom<source::Enum>>::try_from(from.some_enum)
.map_err(MyStructFromSourceStructError::SomeEnumToMyEnum)?
.into();
let some_renamed_string = Some(from.some_string)
.filter(|x| !x.is_empty())
.ok_or(MyStructFromSourceStructError::EmptyString)?
.into();
Self {
some_passthrough,
some_option,
some_vec,
some_enum,
some_renamed_string,
}
})
}
}
#[derive(Debug, thiserror::Error)]
pub(crate) enum MyEnumError {
#[error("a to i 8: {0:?}")]
AToI8(<i8 as TryFrom<i32>>::Error),
#[error("renamed to i 16: {0:?}")]
RenamedToI16(<i16 as TryFrom<i32>>::Error),
#[error("d")]
D,
#[error("e")]
E,
}
impl TryFrom<source::Enum> for MyEnum {
type Error = MyEnumError;
fn try_from(from: source::Enum) -> Result<Self, Self::Error> {
Ok(
match from {
source::Enum::D { .. } => return Err(MyEnumError::D),
source::Enum::E => return Err(MyEnumError::E),
source::Enum::A(f0) => {
let f0 = f0.into();
Self::A(f0)
}
source::Enum::B { a, b } => {
let a = <i8 as TryFrom<i32>>::try_from(a)
.map_err(MyEnumError::AToI8)?
.into();
let renamed = <i16 as TryFrom<i32>>::try_from(b)
.map_err(MyEnumError::RenamedToI16)?
.into();
Self::B { a, renamed }
}
source::Enum::C => Self::C,
},
)
}
}
依赖关系
~1.3–1.8MB
~34K SLoC