2 个不稳定版本

0.2.0 2024 年 4 月 25 日
0.1.0 2023 年 12 月 9 日

#374值格式化

Download history 66/week @ 2024-04-19 47/week @ 2024-04-26 1/week @ 2024-05-03 6/week @ 2024-05-17 5/week @ 2024-05-24 2/week @ 2024-05-31 2/week @ 2024-07-05

每月 116 次下载

MIT 许可证

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 StringToString

  • 指定可以从/转换为的 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