{"id":19782431,"url":"https://github.com/steadylearner/born","last_synced_at":"2025-04-30T22:30:53.491Z","repository":{"id":57523113,"uuid":"266154463","full_name":"steadylearner/born","owner":"steadylearner","description":"Remove code duplication from Struct and Enum with functional macros.","archived":false,"fork":false,"pushed_at":"2023-05-10T09:55:03.000Z","size":131,"stargazers_count":49,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-06T05:34:19.786Z","etag":null,"topics":["macros","reuse-fields","rust"],"latest_commit_sha":null,"homepage":"https://docs.rs/born","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/steadylearner.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-22T16:14:07.000Z","updated_at":"2025-02-07T10:45:34.000Z","dependencies_parsed_at":"2022-08-28T06:00:23.993Z","dependency_job_id":null,"html_url":"https://github.com/steadylearner/born","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steadylearner%2Fborn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steadylearner%2Fborn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steadylearner%2Fborn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steadylearner%2Fborn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/steadylearner","download_url":"https://codeload.github.com/steadylearner/born/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251791491,"owners_count":21644402,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["macros","reuse-fields","rust"],"created_at":"2024-11-12T06:05:08.429Z","updated_at":"2025-04-30T22:30:53.191Z","avatar_url":"https://github.com/steadylearner.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[trybuild]: https://github.com/dtolnay/trybuild\n[macrotest]: https://github.com/eupn/macrotest\n[axum]: https://github.com/tokio-rs/axum\n\nReuse common parts of Struct and Enum\n=============\n\n\u003c!-- Include donate button if people find it useful later. --\u003e\n\n\u003c!-- [![Build Status](https://travis-ci.org/steadylearner/born.svg?branch=master)](https://travis-ci.org/steadylearner/born) --\u003e\n[![Image Crate](https://img.shields.io/crates/v/born.svg)](https://crates.io/crates/born)\n[![Image Doc](https://img.shields.io/badge/rust-documentation-blue.svg)](https://docs.rs/born/\n)\n\nIt provides functional macros to reuse fields from [Struct](https://doc.rust-lang.org/std/keyword.struct.html) and [Enum](https://doc.rust-lang.org/std/keyword.enum.html) definition.\n\n```toml\n[dependencies]\nborn = { git = \"https://github.com/steadylearner/born\", branch = \"master\" }\n```\n\n[![born crate example](https://raw.githubusercontent.com/steadylearner/born/master/unite_rust_and_python_with_born.png)](https://raw.githubusercontent.com/steadylearner/born/master/unite_rust_and_python_with_born.png)\n\n\u003cbr\u003e\n\n## Why this library?\n\nYou can define common fields in Rust struct and enum once and reuse them to remove code duplication. Use it when you want to reuse the same fields for the structs like the example below.\n\n```rust\nuse born::{\n    nested_macro,\n    public_struct,\n};\n\npublic_struct!(\n    pub struct UserBase {\n        username: String,\n        email: String,\n        full_name: Option\u003cString\u003e,\n    }\n);\n\nUserBase!(\n    pub struct UserIn {\n        pub password: String,\n    }\n);\n\n// Reuse with the same fields.\nUserBase!(\n    pub struct UserOut\n);\n\nUserBase!(\n    pub struct UserInDB {\n        pub hashed_password: String,\n    }\n);\n```\n\nCompare it with Python code below from [FAST API](https://fastapi.tiangolo.com/tutorial/extra-models/#reduce-duplication) that inspired this library.\n\n```py\nfrom pydantic import BaseModel, EmailStr\n\nclass UserBase(BaseModel):\n    username: str\n    email: EmailStr\n    full_name: str = None\n\n\nclass UserIn(UserBase):\n    password: str\n\n# Reuse with the same fields.\nclass UserOut(UserBase):\n    pass\n\n\nclass UserInDB(UserBase):\n    hashed_password: str\n```\n\nYou can see almost same thing is done here to remove code duplication in both parts.\n\nBut, different from Python, there is no inheritance of fields with macros from **born**. It is lazily built(born) by your first struct or enum definition.\n\nEverything made from them are completely irrelevant to each other except they share the same definition. There is no memory share or something like that.\n\nThe macros from this library are **lazy struct and enum builders** to remove code duplication. It is possible with the power of the Rust macro.\n\n## Examples\n\nHere, macros to build public struct and enum are used.\n\nIf you want to build private struct and enum, just use macros that start with private and shouldn't use `pub` inside.\n\n### Struct\n\nSay you build a simple demo web server to send private messages.\n\n```rust\nuse born::{\n    nested_macro,\n    public_struct,\n};\n\npublic_struct!(\n    // pub is required here before struct\n    pub struct MessageBase {\n        pub text: String\n        // pub text: String // , is not required for the struct definition.\n    }\n);\n\nMessageBase!(\n    #[derive(Debug, Clone, PartialEq)]\n    pub struct Message {\n        pub read: bool,\n        // read: bool, // pub is optional.\n    }\n);\n\nimpl Message {\n    fn update_text(\u0026mut self, new_message: String) {\n        self.text = new_message\n    }\n    fn read(\u0026mut self) {\n        if self.read == false {\n            self.read = true;\n        }\n    }\n}\n\nMessageBase!(\n    #[derive(Debug, Clone, PartialEq)]\n    pub struct MessageCreateRequest\n);\n\nMessageBase!(\n    // #[derive(Debug, Clone, PartialEq)]\n    pub struct MessageUpdateRequest\n);\n\nfn main() {\n    let message_create_request = MessageCreateRequest {\n        text: \"I am Steadylearner and 'born' is the crate name.\".into(),\n    };\n\n    let mut message = Message {\n        text: message_create_request.text,\n        read: false,\n    };\n    println!(\"{:#?}\", \u0026message);\n\n    assert_eq!(message, message.clone());\n\n    let message_update_request = MessageUpdateRequest {\n        text: \"Reuse fields with macros from 'born'.\".into(),\n    };\n\n    message.update_text(message_update_request.text);\n    println!(\"{:#?}\", \u0026message);\n\n    message.read();\n    println!(\"{:#?}\", \u0026message);\n}\n```\n\nYou can also use the public_struct! and private_struct! with serde derive. For example, you can rename fields to camelCase with `#[serde(rename_all = \"camelCase\")]` etc.\n\n```rust\n// Cargo.toml\n// born = { git = \"https://github.com/steadylearner/born\", branch = \"master\" }\n\nuse born::{\n    nested_macro,\n    public_struct,\n};\n\nuse serde::{Serialize, Deserialize}; \n\npublic_struct!(\n    pub struct PostBase {\n        pub user_id: i8,\n        pub title: String,\n        pub body: String,\n    }\n);\n\nPostBase!(\n    #[derive(Serialize, Deserialize, Debug)]\n    #[serde(rename_all = \"camelCase\")]\n    pub struct Post {\n        pub id: i8,\n    }\n);\n```\n\n### Enum\n\n[Compare it with the code example from the Rust documenation.](https://doc.rust-lang.org/stable/rust-by-example/custom_types/enum.html)\n\n```rust\nuse born::{\n    nested_macro,\n    private_enum,\n};\n\nprivate_enum!(\n    enum WebEventBase {\n        PageLoad,\n        PageUnload, // , here is required if you want to extend it.\n    }\n);\n\nWebEventBase!(\n    // #[derive(Debug, Clone, PartialEq)]\n    enum WebEvent {\n        KeyPress(char),\n        Click { x: i64, y: i64 },\n        Paste(String),\n    }\n);\n\nfn inspect(event: WebEvent) {\n    match event {\n        WebEvent::PageLoad =\u003e println!(\"page loaded\"),\n        WebEvent::PageUnload =\u003e println!(\"page unloaded\"),\n        WebEvent::KeyPress(c) =\u003e println!(\"pressed '{}'.\", c),\n        WebEvent::Paste(s) =\u003e println!(\"pasted \\\"{}\\\".\", s),\n        WebEvent::Click { x, y } =\u003e {\n            println!(\"clicked at x={}, y={}.\", x, y);\n        },\n    }\n}\n\nfn main() {\n    let pressed = WebEvent::KeyPress('x');\n    let pasted  = WebEvent::Paste(\"my text\".to_owned());\n    let click   = WebEvent::Click { x: 20, y: 80 };\n    let load    = WebEvent::PageLoad;\n    let unload  = WebEvent::PageUnload;\n\n    inspect(pressed);\n    inspect(pasted);\n    inspect(click);\n    inspect(load);\n    inspect(unload);\n}\n```\n\n\u003cbr\u003e\n\n## Details\n\n- Each struct and enum created from the macros are completely unrelevant to each other except they are built(born) from the same definition.\n\n- When you use `private_struct!` and `private_enum!`, you can't use pub keyword in it and others use them. [It wouldn't be logical if a private struct or private enum can have public fields.](https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#making-structs-and-enums-public)\n\n- `nested_macro!` is required to use the other macros from this crate. It is used to make a macro that creates other macros.\n\n```rust\nmacro_rules! nested_macro {\n    ($($body:tt)*) =\u003e {\n        macro_rules! __nested_macro { $($body)+ }\n        __nested_macro!($);\n    }\n}\n```\n\n- You can also use attributes for the common parts.\n\n```rust\n// Cargo.toml\n// born = { git = \"https://github.com/steadylearner/born\", branch = \"master\" }\n\nuse born::{nested_macro, public_struct};\nuse serde::{Serialize, Deserialize}; \n\npublic_struct!(\n    #[derive(Deserialize, Serialize, Debug)]\n    pub struct PersonBase {\n        #[serde(rename = \"person_email\")]\n        email: String,\n    }\n);\n\nPersonBase!(pub struct Person); // You have to call it to use.\nPersonBase!(pub struct PersonWithName {\n    #[serde(rename = \"person_name\")]\n    name: String,\n});\n```\n\n```rs\n// Cargo.toml\n// born = { git = \"https://github.com/steadylearner/born\", branch = \"master\" }\n\nuse born::{nested_macro, public_enum};\nuse serde::{Serialize, Deserialize};\n\npublic_enum!(\n    #[derive(Debug, Serialize, Deserialize)]\n    pub enum PersonBase {\n        #[serde(rename = \"person_type_struct\")] \n        TypeStruct { email: String, name: String },\n        Type,\n    }\n);\n\nPersonBase!(pub enum Person {\n    TypeExtension\n});\n\nPersonBase!(pub enum PersonSerde {\n    #[serde(rename = \"person_type_extension\")] \n    TypeExtensionWithSerde\n});\n```\n\n\u003cbr\u003e\n\n## Why not attribute macro?\n\n- [You can reuse the fields with attribute macro also.](https://github.com/steadylearner/born-attribute) But, you need some dependencies.\n\n- [If you want more, please read the official documenation about procedural macros.](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)\n\n\u003cbr\u003e\n\n## How to test it\n\n```console\n$git clone git@github.com:steadylearner/born.git \u0026\u0026 cargo test pass\n```\n\n1. `$cargo install cargo-expand` and `$cargo test pass` to run passing tests.\n2. `$cargo test fail` to run failing tests. You need to install [trybuild] first.\n\nIf you want to see how the macros from this package expand, use `$cargo test macros`. You need to install [rustfmt](https://github.com/rust-lang/rustfmt) and [cargo-expand](https://github.com/dtolnay/cargo-expand) to use it before.\n\nWhen you use serde or others with it, cargo-expand command might show errors but that doesn't mean that the code from this package will fail.\n\n```console\n$rustup component add rustfmt \u0026\u0026 cargo install cargo-expand\n```\n\n[macrotest] is based on [trybuild]. They are not that compatible to test with a single command and take long time.\n\nThey make cargo to redownload the dependendencies and recompile everytime. For that reason, there are commands to test them separately.\n\n## Examples\n\nYou can also test an example that works with [axum] with these commands.\n\n```console\n$cargo run --example axum_example\n```\n\nThen, use this CURL command to see it work.\n\n```console\n$curl -X POST \\\n   -H \"Content-Type: application/json\" \\\n   -d '{ \"username\": \"username\" }' \\\n   http://localhost:3000/users\n```\n\nYou can also use [the blog](/examples/blog/) example. You can visit the [Steadylearner](https://www.steadylearner.com) website based on it also.\n\n#### License\n\n\u003csup\u003e\nLicensed under either of \u003ca href=\"LICENSE-APACHE\"\u003eApache License, Version\n2.0\u003c/a\u003e or \u003ca href=\"LICENSE-MIT\"\u003eMIT license\u003c/a\u003e at your option.\n\u003c/sup\u003e\n\n\u003cbr\u003e\n\n\u003csub\u003e\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in this crate by you, as defined in the Apache-2.0 license, shall\nbe dual licensed as above, without any additional terms or conditions.\n\u003c/sub\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteadylearner%2Fborn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsteadylearner%2Fborn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteadylearner%2Fborn/lists"}