2 个不稳定版本
0.2.0 | 2024 年 4 月 25 日 |
---|---|
0.1.0 | 2023 年 12 月 9 日 |
#374 在 值格式化 中
每月 116 次下载
65KB
996 行
Racros
Rust 宏集合。
内容
AutoDebug
适用于结构和枚举,类似于 std::fmt::Debug
,但支持一些自定义。
- 在打印消息中指定字段名或值。
- 忽略指定的字段。
- 在指定的字段上使用
std::fmt::Display
而不是 [std::fmt:;Debug
]。 - 设置类似于打印元组或结构的打印样式。
基本用法
#[derive(AutoDebug)]
使结构获得风格 debug 实现。
结构属性
#[debug_style = tuple]
使结构获得类似元组的 debug 实现。默认为结构样式。#[debug_format = display]
在字段上使用Display
特性。默认为 debug 格式。
结构字段属性
#[debug_name = "foo"]
在结构debug_style
中用 "foo" 覆盖字段名。#[debug_value = "foo"]
使用 "foo" 覆盖字段值。#[debug_ignore]
将忽略输出中的此字段。#[debug_debug]
将在输出中使用此字段的 [Debug] 特性实现。#[debug_display]
将在输出中使用此字段的 [Display] 特性实现。
示例
对于自定义类型 MyType
,在 Display
中打印 display MyType
,在 Debug
中打印 debug MyType
。
struct MyType {}
impl std::fmt::Debug for MyType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("debug MyType")
}
}
impl std::fmt::Display for MyType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("display MyType")
}
}
默认情况下为 debug_struct
中的每个字段生成带有 "{:#?}" 的实现。
#[derive(AutoDebug)]
struct Foo1 {
#[debug_name = "my_foo1"] // Use "my_foo1" as strcut field name.
foo1: MyType,
#[debug_ignore] // Ignore this field.
foo2: MyType,
#[debug_display] // Use `Display` implementation for this field.
foo3: MyType,
#[debug_value = "foo4, MyType"] // Use "foo4, MyType" as struct field value.
foo4: MyType,
}
#[derive(AutoDebug)]
#[debug_format = "display"] // Default implementation use "{}" for each field.
#[debug_style = "tuple"] // Output style set to `debug_tuple`.
struct Foo2 {
#[debug_debug] // Use `Debug` implementation for this field.
foo1: MyType,
foo2: MyType,
}
let foo1 = Foo1 {
foo1: MyType {},
foo2: MyType {},
foo3: MyType {},
foo4: MyType {},
};
assert_eq!(
std::fmt::format(format_args!("{:#?}", foo1)),
r#"Foo1 {
my_foo1: debug MyType,
foo3: display MyType,
foo4: "foo4, MyType",
}"#
);
let foo2 = Foo2 {
foo1: MyType {},
foo2: MyType {},
};
assert_eq!(
std::fmt::format(format_args!("{:#?}", foo2)),
r#"Foo2(
debug MyType,
display MyType,
)"#
);
AutoStr
为具有以下功能的枚举实现 TryFrom
String
和 ToString
- 指定可以从/转换为的
String
值。 - 允许从多个
String
值转换。 - 设置默认转换样式
小写
大写
驼峰式
帕斯卡式
下划线
全大写
对于以下代码
#[derive(AutoStr)]
enum MyEnum {
E1,
E2,
E3,
}
AutoStr
将生成代码
impl TryFrom<&str> for MyEnum {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"E1" => Ok(MyEnum::E1),
"E2" => Ok(MyEnum::E2),
"E3" => Ok(MyEnum::E3),
_ => Err(Self::Error::from(
format("failed to convert to {0} :invalid value","MyEnum")
))
}
}
}
impl ToString for MyEnum {
fn to_string(&self) -> String {
match self {
MyEnum::E1 => "E1".to_string(),
MyEnum::E2 => "E2".to_string(),
MyEnum::E3 => "E3".to_string(),
}
}
}
字符串格式可以设置为
小写
大写
驼峰式
帕斯卡式
下划线
SCREAMING_CASE
通过向枚举添加一个#[autorule = "xxxx"]
属性
#[derive(AutoStr)]
#[autorule = "lowercase"]
enum MyEnum {
E1,
E2,
E3,
}
impl TryFrom<&str> for MyEnum {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"e1" => Ok(MyEnum::E1),
"e2" => Ok(MyEnum::E2),
"e3" => Ok(MyEnum::E3),
_ => Err(Self::Error::from(
format("failed to convert to {} :invalid value","MyEnum")
))
}
}
}
impl ToString for MyEnum {
fn to_string(&self) -> String {
match self {
MyEnum::E1 => "e1".to_string(),
MyEnum::E2 => "e2".to_string(),
MyEnum::E3 => "e3".to_string(),
}
}
}
此外,添加 #[str(...)]
属性到枚举字段将覆盖默认格式。
#[derive(AutoStr)]
#[autorule = "lowercase"]
enum MyEnum {
#[str("e1", "E1")]
E1,
E2,
#[str("e3", "ee")]
E3,
}
impl TryFrom<&str> for MyEnum {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"e1" | "E1" => Ok(MyEnum::E1),
"e2" => Ok(MyEnum::E2),
"e3" | "ee"=> Ok(MyEnum::E3),
_ => Err(Self::Error::from(
format("failed to convert to {} :invalid value","MyEnum")
))
}
}
}
impl ToString for MyEnum {
fn to_string(&self) -> String {
match self {
MyEnum::E1 => "e1".to_string(),
MyEnum::E2 => "e2".to_string(),
MyEnum::E3 => "e3".to_string(),
}
}
}
支持嵌套枚举
enum MyEnum4 {
E41(MyEnum),
E42(MyEnum2),
}
impl TryFrom<&str> for MyEnum4 {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
_ => {
let mut fallback_field: Option<&str> = None;
let mut fallback_result: Option<Self> = None;
if let Ok(v) = MyEnum::try_from(value) {
if fallback_result.is_some() {
return Err(
Self::Error::from({
format!(
"#[str(...)] attribute not set and fallback guess is ambiguous: both {} and {} can accept this convert from \"{}\"",
fallback_field.unwrap(),
"MyEnum",
value,
)
}),
);
}
fallback_field = Some("MyEnum");
fallback_result = Some(MyEnum4::E41(v));
}
if let Ok(v) = MyEnum2::try_from(value) {
if fallback_result.is_some() {
return Err(
Self::Error::from({
format_args!(
"#[str(...)] attribute not set and fallback guess is ambiguous: both {} and {} can accept this convert from \"{}\"",
fallback_field.unwrap(),
"MyEnum2",
value,
)
}),
);
}
fallback_field = Some("MyEnum2");
fallback_result = Some(MyEnum4::E42(v));
}
match fallback_result {
Some(v) => Ok(v),
None => {
Err(
Self::Error::from({
format_args!(
"failed to convert to {} :invalid value \"{}\"",
"MyEnum4",
value,
)
}),
)
}
}
}
}
}
}
impl ToString for MyEnum4 {
fn to_string(&self) -> String {
match self {
MyEnum4::E41(v) => v.to_string(),
MyEnum4::E42(v) => v.to_string(),
}
}
}
CopyWith
为装饰类型添加 copy_with
函数,如果该值不是 default
值,则从另一个 Self
复制值。
基本用法
对于以下结构,生成
struct MyStruct {
foo1: i8,
foo2: String,
foo3: Option<String>,
}
impl MyStruct {
fn copy_with(&mut self, other: &Self) {
if other.foo1 != i8::default() {
self.foo1 = other.foo1.clone();
}
if other.foo2 != String::default() {
self.foo2 = other.foo2.clone();
}
if other.foo3 != Option::default() {
self.foo3 = other.foo3.clone();
}
}
}
字段属性
将 #[copy]
属性添加到字段将尝试在该字段上调用 .copy_with()
而不是直接比较值。
示例
use racros::CopyWith;
#[derive(Clone, Default, CopyWith)]
struct MyStruct {
foo1: i8,
foo2: String,
foo3: Option<String>,
}
#[derive(CopyWith)]
struct MyStruct2 {
#[copy]
bar1: MyStruct,
}
let s1 = MyStruct::default();
let mut s11 = MyStruct::default();
let s2 = MyStruct {
foo1: 64,
foo2: String::from("hello world"),
foo3: Some(String::from("hello world")),
};
let mut s21 = MyStruct {
foo1: 64,
foo2: String::from("hello world"),
foo3: Some(String::from("hello world")),
};
s11.copy_with(&s2);
assert_eq!(s11.foo1, s2.foo1); // Copy s2.foo1 to s11.foo1
assert_eq!(s11.foo2, s2.foo2); // Copy s2.foo2 to s11.foo2
assert_eq!(s11.foo3, s2.foo3); // Copy s2.foo3 to s11.foo3
s21.copy_with(&s1);
assert_eq!(s21.foo1, s2.foo1); // Not copy because s1.foo1 is default value.
assert_eq!(s21.foo2, s2.foo2); // Not copy because s1.foo2 is default value.
assert_eq!(s21.foo3, s2.foo3); // Not copy because s1.foo3 is default value.
let mut s31 = MyStruct2 {
bar1: MyStruct::default(),
};
let s32 = MyStruct2 {
bar1: MyStruct {
foo1: 64,
foo2: String::from("hello world"),
foo3: Some(String::from("hello world")),
},
};
s31.copy_with(&s32); // Here use `s32.copy_with()` because bar1 has `#[copy]` attribute.
assert_eq!(s31.bar1.foo1, s2.foo1);
assert_eq!(s31.bar1.foo2, s2.foo2);
assert_eq!(s31.bar1.foo3, s2.foo3);
BundleText
在编译时将文本内容或命令输出捆绑到静态 str,并在运行时使用。
用法
- 捆绑文件:#[bundle(name = "get_file", file = "file/path")]
- 捆绑命令:#[bundle(name = "get_rustc_version", command = "rustc --version")]
示例
use std::io::Read;
use std::process::Stdio;
use racros::BundleText;
#[derive(BundleText)]
#[bundle(name = "some_file", file = "data/text")]
#[bundle(name = "my_rustc_version", command = "rustc --version")]
enum Bundler{}
assert_eq!(
Bundler::some_file(),
r#"Some Text
To Read"#
);
let mut stdout = String::new();
std::process::Command::new("rustc")
.arg("--version")
.stdout(Stdio::piped())
.spawn()
.unwrap()
.stdout
.unwrap()
.read_to_string(&mut stdout)
.expect("failed to run rustc");
assert_eq!(Bundler::my_rustc_version(), stdout);
依赖项
~275–720KB
~17K SLoC