#枚举 # #结构体 #字段 #函数式 #重用 #定义

no-std born

使用函数式宏在结构体和枚举定义中重用字段

2 个版本

0.0.1 2020 年 5 月 25 日
0.0.0 2020 年 5 月 22 日

#34#重用

MIT/Apache

28KB
203

重用(结构体,枚举)

Build Status Image Crate Image Doc

它提供了函数式宏,可以从 结构体枚举 定义中重用字段。

[dependencies]
born = "0.0.1"

born crate example


为什么选择这个库?

您可以在 Rust 结构体和枚举中一次性定义常用字段,并通过重用它们来减少代码重复。当您想重用相同字段的多个结构体时,可以使用此功能,如下例所示。

use born::{
    nested_macro,
    public_struct,
};

public_struct!(
    pub struct UserBase {
        username: String,
        email: String,
        full_name: Option<String>,
    }
);

UserBase!(
    pub struct UserIn {
        pub password: String,
    }
);

// Reuse with the same fields.
UserBase!(
    pub struct UserOut
);

UserBase!(
    pub struct UserInDB {
        pub hashed_password: String,
    }
);

与以下来自 FAST API 的 Python 代码进行比较,该代码启发了这个库。

from pydantic import BaseModel, EmailStr

class UserBase(BaseModel):
    username: str
    email: EmailStr
    full_name: str = None


class UserIn(UserBase):
    password: str

# Reuse with the same fields.
class UserOut(UserBase):
    pass


class UserInDB(UserBase):
    hashed_password: str

您可以看到,在这两部分中,都执行了几乎相同的操作来减少代码重复。

但与 Python 不同,宏不会从 born 继承字段。它通过您的第一个结构体或枚举定义懒加载构建。

所有由此生成的结构体和枚举之间完全无关,除了它们都基于相同的定义之外。它们之间没有内存共享或类似的东西。

此库中的宏是 懒加载结构体和枚举构建器,用于消除代码重复。这是通过 Rust 宏的力量实现的。

示例

在此,使用了用于构建公共结构体和枚举的宏。

如果您想构建私有结构体和枚举,只需使用以 private 开头的宏即可,并且不应在内部使用 pub

结构体

例如,您构建一个简单的用于发送私密消息的演示 Web 服务器。

use born::{
    nested_macro,
    public_struct,
};

public_struct!(
    // pub is required here before struct
    pub struct MessageBase {
        pub text: String
        // pub text: String // , is not required for the struct definition.
    }
);

MessageBase!(
    #[derive(Debug, Clone, PartialEq)]
    pub struct Message {
        pub read: bool,
        // read: bool, // pub is optional.
    }
);

impl Message {
    fn update_text(&mut self, new_message: String) {
        self.text = new_message
    }
    fn read(&mut self) {
        if self.read == false {
            self.read = true;
        }
    }
}

MessageBase!(
    #[derive(Debug, Clone, PartialEq)]
    pub struct MessageCreateRequest
);

MessageBase!(
    // #[derive(Debug, Clone, PartialEq)]
    pub struct MessageUpdateRequest
);

fn main() {
    let message_create_request = MessageCreateRequest {
        text: "I am Steadylearner and 'born' is the crate name.".into(),
    };

    let mut message = Message {
        text: message_create_request.text,
        read: false,
    };
    println!("{:#?}", &message);

    assert_eq!(message, message.clone());

    let message_update_request = MessageUpdateRequest {
        text: "Reuse fields with macros from 'born'.".into(),
    };

    message.update_text(message_update_request.text);
    println!("{:#?}", &message);

    message.read();
    println!("{:#?}", &message);
}

枚举

与 Rust 文档中的代码示例进行比较。

use born::{
    nested_macro,
    private_enum,
};

private_enum!(
    enum WebEventBase {
        PageLoad,
        PageUnload, // , here is required if you want to extend it.
    }
);

WebEventBase!(
    // #[derive(Debug, Clone, PartialEq)]
    enum WebEvent {
        KeyPress(char),
        Click { x: i64, y: i64 },
        Paste(String),
    }
);

fn inspect(event: WebEvent) {
    match event {
        WebEvent::PageLoad => println!("page loaded"),
        WebEvent::PageUnload => println!("page unloaded"),
        WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
        WebEvent::Paste(s) => println!("pasted \"{}\".", s),
        WebEvent::Click { x, y } => {
            println!("clicked at x={}, y={}.", x, y);
        },
    }
}

fn main() {
    let pressed = WebEvent::KeyPress('x');
    let pasted  = WebEvent::Paste("my text".to_owned());
    let click   = WebEvent::Click { x: 20, y: 80 };
    let load    = WebEvent::PageLoad;
    let unload  = WebEvent::PageUnload;

    inspect(pressed);
    inspect(pasted);
    inspect(click);
    inspect(load);
    inspect(unload);
}

详情

  • 从这些宏创建的每个结构体和枚举之间完全无关,除非它们都基于相同的定义。

  • 当您使用 private_struct!private_enum! 时,您不能在内部使用 pub 关键字,并且其他人可以使用它们。 如果私有结构体或枚举可以具有公共字段,则这就不合逻辑。

  • nested_macro! 是必需的,用于使用此 crate 中的其他宏。它用于创建创建其他宏的宏。

macro_rules! nested_macro {
    ($($body:tt)*) => {
        macro_rules! __nested_macro { $($body)+ }
        __nested_macro!($);
    }
}

为什么不使用属性宏?


如何测试

$git clone [email protected]:steadylearner/born.git && cargo test pass
  1. $cargo test pass 运行通过测试。
  2. $cargo test fail 运行失败测试。您需要先安装 trybuild

如果您想查看此包中宏的展开方式,请使用 $cargo test macros。在使用它之前,您需要安装 rustfmtcargo-expand

$rustup component add rustfmt && cargo install cargo-expand

macrotest 基于 trybuild。它们在单个命令中测试不兼容,并且需要很长时间。

它们使 cargo 每次都重新下载依赖项并重新编译。因此,存在单独测试它们的命令。

许可证

根据您的选择,受 Apache License,版本 2.0MIT 许可证 许可。
除非您明确表示,否则根据 Apache-2.0 许可证定义,您有意提交以包含在此软件包中的任何贡献,应如上所述双重许可,无需任何附加条款或条件。

无运行时依赖项