{"id":19074792,"url":"https://github.com/seokminhong/builder-pattern","last_synced_at":"2025-04-29T23:30:02.483Z","repository":{"id":46623761,"uuid":"396889498","full_name":"SeokminHong/builder-pattern","owner":"SeokminHong","description":"A derivable macro for declaring a builder pattern.","archived":false,"fork":false,"pushed_at":"2023-12-21T06:05:33.000Z","size":199,"stargazers_count":29,"open_issues_count":7,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-18T19:41:33.735Z","etag":null,"topics":["builder-pattern","macro","rust"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/builder-pattern","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SeokminHong.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-08-16T17:02:23.000Z","updated_at":"2024-12-31T16:31:25.000Z","dependencies_parsed_at":"2023-12-21T09:14:43.064Z","dependency_job_id":null,"html_url":"https://github.com/SeokminHong/builder-pattern","commit_stats":{"total_commits":142,"total_committers":1,"mean_commits":142.0,"dds":0.0,"last_synced_commit":"d75ca48dbc41769c2347f601812e13d56b827972"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeokminHong%2Fbuilder-pattern","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeokminHong%2Fbuilder-pattern/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeokminHong%2Fbuilder-pattern/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeokminHong%2Fbuilder-pattern/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SeokminHong","download_url":"https://codeload.github.com/SeokminHong/builder-pattern/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251599549,"owners_count":21615542,"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":["builder-pattern","macro","rust"],"created_at":"2024-11-09T01:52:29.932Z","updated_at":"2025-04-29T23:30:02.461Z","avatar_url":"https://github.com/SeokminHong.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# builder-pattern\n\n[![creates.io](https://img.shields.io/crates/v/builder-pattern?logo=rust)](https://crates.io/crates/builder-pattern)\n[![API Docs](https://docs.rs/builder-pattern/badge.svg?logo=docs-rs)](https://docs.rs/builder-pattern/)\n\n[![Build Status (Windows)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_windows.yml/badge.svg)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_windows.yml)\n[![Build Status (MacOS)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_macos.yml/badge.svg)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_macos.yml)\n[![Build Status (Ubuntu)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_ubuntu.yml/badge.svg)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_ubuntu.yml)\n[![Build Status (WebAssembly)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_wasm.yml/badge.svg)](https://github.com/SeokminHong/builder-pattern/actions/workflows/build_wasm.yml)\n\nA derivable macro for declaring a builder pattern. This crate is highly inspired by [derive_builder](https://github.com/colin-kiegel/rust-derive-builder).\n\n## Usage\n\n```rust\nuse builder_pattern::Builder;\n\n#[derive(Builder)]\nstruct Person {\n    #[into]\n    name: String,\n    age: i32,\n    #[default(Gender::Nonbinary)]\n    #[setter(value, async)]\n    gender: Gender,\n}\n\nlet p1 = Person::new()          // PersonBuilder\u003c(), (), (), ...\u003e\n    .name(String::from(\"Joe\"))  // PersonBuilder\u003cString, (), (), ...\u003e\n    .age(27)                    // PersonBuilder\u003cString, i32, (), ...\u003e\n    .build();                   // Person\n\n// Order does not matter.\nlet p2 = Person::new()          // PersonBuilder\u003c(), (), (), ...\u003e\n    .age(32)                    // PersonBuilder\u003c(), i32, (), ...\u003e\n    // `\u0026str` is implicitly converted into `String`\n    // because of `into` attribute!\n    .name(\"Jack\")               // PersonBuilder\u003cString, i32, (), ...\u003e\n    .gender(Gender::Male)       // PersonBuilder\u003cString, i32, Gender, ...\u003e\n    .build();                   // Person\n\nlet p3 = Person::new()          // PersonBuilder\u003c(), (), (), ...\u003e\n    .age(32)                    // PersonBuilder\u003c(), i32, (), ...\u003e\n    // `\u0026str` is implicitly converted into `String`\n    // because of `into` attribute!\n    .name(\"Jack\")               // PersonBuilder\u003cString, i32, (), ...\u003e\n    .gender_async(|| async {\n        Gender::Male\n    })                          // PersonBuilder\u003cString, i32, Gender, ...\u003e\n    .build()                    // Future\u003cPerson\u003e\n    .await;                     // Person\n\n// `name` field required - Compilation error.\nlet p4 = Person::new()          // PersonBuilder\u003c(), (), (), ...\u003e\n    .age(15)                    // PersonBuilder\u003c(), i32, (), ...\u003e\n    .build();\n```\n\n## Get Started\n\nAdd `builder-pattern` to `Cargo.toml`.\n\n```toml\n# Cargo.toml\n[dependencies]\nbuilder-pattern = \"0.4\"\n```\n\nThe crate feature `future` is enabled by default. If you don't need asynchronous features, you can disable it.\n\n```toml\n# Cargo.toml\n[dependencies]\nbuilder-pattern = { version = \"0.4\", default-features = false }\n```\n\n## Features\n\n- **Chaining**: Can make structure with chained setters.\n- **Complex types are supported**: Lifetime, trait bounds, and where clauses are well supported.\n- **Type safety**: Autocompletion tools can suggest correct setters to build the struct. Also, `build` function is allowed only the all of required fields are provided. **No Result**, **No Unwrap**. Just use it.\n- **Lazy evaluation and asynchronous**: Lazy evaluation and asynchronous are supported. The values will be evaluated when the structure is built.\n- **No additional tasks**: There's no additional constraints to use the macro. Any structures and fields are allowed.\n- **Auto-generated documentation**: Documentation for the builder functions are automatically generated.\n\n## Attributes\n\n### `#[default(expr)]`\n\nA field having this attribute will be considered as optional, and the `expr` will be evaluated as a default value of the field. `build` function can be called without providing this field.\n\n```rust\n#[derive(Builder)]\nstruct Test {\n    #[default]\n    pub a: i32,\n    pub b: \u0026'static str,\n}\n\nlet t1 = Test::new().b(\"Hello\").build(); // The structure can be built without `a`.\nlet t2 = Test::new().b(\"Hi\").a(3).build();\n```\n\n### `#[default_lazy(expr)]`\n\nA field having this attribute will be considered as optional, and the `expr` will be lazily evaluated as a default value of the field. `expr` should be a function or a closure having no arguments.\n\n```rust\n#[derive(Builder)]\nstruct Test {\n    #[default_lazy(|| some_heavy_task() + 3)]\n    pub a: i32,\n    #[default_lazy(some_heavy_task)]\n    pub b: i32,\n}\n\nlet t1 = Test::new().build(); // The structure can be built without `a` and `b`.\nlet t2 = Test::new().a(3).build();\n```\n\n### `#[hidden]`\n\nIf this attribute is present, the builder function would not be generated for the field. This field requires `default` or `default_lazy` attribute.\n\nExample:\n\n```rust\n#[derive(Builder)]\nstruct Test {\n    #[default(Uuid::new_v4())]\n    #[hidden]\n    id: Uuid,\n    name: String,\n}\n\nlet test1 = Test::new()         // TestBuilder\u003c(), (), ...\u003e\n    .name(String::from(\"Joe\"))  // TestBuilder\u003cString, (), ...\u003e\n    .build();                   // Test\n\nlet test2 = Test::new()         // TestBuilder\u003c(), (), ...\u003e\n    .name(String::from(\"Jack\")) // TestBuilder\u003cString, (), ...\u003e\n    // Error: `id` function is not generated.\n    .id(Uuid::parse_str(\"46ebd0ee-0e6d-43c9-b90d-ccc35a913f3e\").unwrap())\n    .build();\n```\n\n### `#[public]`\n\nIf this attribute is present, a field would be exposed with setter functions even though the\nfield is private. It provides a way to access private fields during the building.\n\nExample:\n\n```rust\nmod test {\n    #[derive(Builder)]\n    pub struct Test {\n        #[public]\n        id: Uuid,\n        pub name: \u0026'static str,\n    }\n}\nuse test::Test;\nlet test1 = Test::new()   // TestBuilder\u003c(), (), ...\u003e\n    .id(Uuid::new_v4())   // TestBuilder\u003cUuid, (), ...\u003e\n    .name(\"Joe\")          // TestBuilder\u003cUuid, \u0026'static str, ...\u003e\n    .build();             // Test\n\nassert_eq!(test1.name, \"Joe\");\nprintln!(\"{}\", test1.id); // Error: `id` is a private field.\n```\n\n### `#[setter(value | lazy | async)]`\n\nIf this attribute presents, it provides specified setters. If it doesn't, only the value setter is provided.\n\n```rust\n#[derive(Builder, Debug)]\nstruct Person {\n    // All kinds of setters are provided.\n    #[setter(value, lazy, async)]\n    name: String,\n    // Only value setter is provided.\n    age: u8,\n    // Only lazy setter is provided.\n    #[setter(lazy)]\n    address: \u0026'static str,\n}\n\nlet p1 = Person::new()\n    .name_async(|| async { String::from(\"Joe\") })\n    .age(15)\n    .address_lazy(|| \"123 Main St\")\n    .build()  // `address` is validated here\n    .await; // `name` is validated here\n```\n\n### `#[into]`\n\nA setter function for a field having this attribute will accept `Into` trait as a parameter. You can use this setter with implicit conversion.\n\nExample:\n\n```rust\n#[derive(Builder)]\nstruct Test {\n    #[into]\n    #[setter(value, lazy)]\n    pub name: String,\n}\n\nlet test1 = Test::new()         // TestBuilder\u003c(), ...\u003e\n    // `\u0026str` is implicitly converted into `String`.\n    .name(\"Hello\")              // TestBuilder\u003cString, ...\u003e\n    .build();                   //\n\nlet test2 = Test::new()         // TestBuilder\u003c(), ...\u003e\n    // `\u0026str` is implicitly converted into `String`.\n    .name_lazy(|| \"Hello\")      // TestBuilder\u003cString, ...\u003e\n    .build();                   // Test\n```\n\n### `#[validator(expr)]`\n\nImplement a validator for a field. `expr` could be a validating function that takes the field's type and returns `Result`.\n\n```rust\n#[derive(Builder)]\nstruct Test {\n    #[validator(is_not_empty)]\n    #[into]\n    pub name: String,\n}\n\nfn is_not_empty(name: String) -\u003e Result\u003cString, \u0026'static str\u003e {\n    if name.is_empty() {\n        Err(\"Name cannot be empty.\")\n    } else {\n        Ok(name)\n    }\n}\n\nlet test1 = Test::new()         // TestBuilder\u003c(), ...\u003e\n    .name(\"Hello\")              // Ok(TestBuilder\u003cString, ...\u003e)\n    .unwrap()                   // TestBuilder\u003cString, ...\u003e\n    .build();                   // Test\n\nlet test2 = Test::new()         // TestBuilder\u003c(), ...\u003e\n    .name(\"\")                   // Err(String{ \"Validation failed: Name cannot be empty.\" })\n    .unwrap()                   // panic!\n    .build();\n```\n\nIf the validator is used with lazy or async setters, it will also validated lazily or asynchronously. So, the setter doesn't return `Result` but it is returned when it is built.\n\n```rust\n\n#[derive(Builder)]\nstruct Test {\n    #[validator(is_not_empty)]\n    #[setter(value, lazy, async)]\n    pub name: \u0026'static str,\n}\n\nlet test1 = Test::new()         // TestBuilder\u003c(), ...\u003e\n    .name_lazy(|| \"Hello\")      // TestBuilder\u003cString, ...\u003e\n    .build()                    // Ok(Test)\n    .unwrap();                  // Test\n\nlet test2 = Test::new()         // TestBuilder\u003c(), ...\u003e\n    .name_async(|| async {\n        \"Hello\".to_string()\n    })                          // TestBuilder\u003cString, ...\u003e\n    .build()                    // Future\u003cResult\u003cTest, Strin\n    .await                      // Ok(Test)\n    .unwrap();                  // Test\n```\n\n## Auto-Generated Documentation\n\nThis crate generates documentation for the builder functions. If you document fields,\nthe builder functions for them also copy the documentation.\n\n### Example\n\nExample code:\n\n```rust\n#[derive(Builder)]\nstruct Test {\n    /// A positive integer.\n    pub positive: i32,\n\n    /// An integer having zero as a default value.\n    #[default(0)]\n    pub zero: i32,\n}\n```\n\nGenerated code:\n\n```rust\nimpl Test {\n    /// Creating a builder.\n    /// ## Required fields\n    /// ### `positive`\n    /// - Type: `i32`\n    ///\n    /// A positive integer.\n    ///\n    /// ## Optional fields\n    /// ### `zero`\n    /// - Type: `i32`\n    /// - Default: `0`\n    ///\n    /// An integer having zero as a default value.\n    fn new() -\u003e TestBuilder\u003c(), ()\u003e {\n        TestBuilder {\n            _phantom: PhantomData,\n            positive: None,\n            zero: Some(0),\n        }\n    }\n}\n\n/// A builder for `Test`.\nstruct TestBuilder\u003cT1, T2\u003e {\n    _phantom: PhantomData\u003c(T1, T2)\u003e,\n    positive: Option\u003ci32\u003e,\n    zero: Option\u003ci32\u003e,\n}\n\nimpl TestBuilder\u003ci32, i32\u003e {\n    fn build(self) -\u003e Test {\n        Test {\n            positive: self.positive.unwrap(),\n            zero: self.zero.unwrap(),\n        }\n    }\n}\n\nimpl\u003cT2\u003e TestBuilder\u003c(), T2\u003e {\n    /// # positive\n    /// - Type: `i32`\n    ///\n    /// A positive integer.\n    pub fn positive(self, value: i32) -\u003e TestBuilder\u003ci32, T2\u003e {\n        TestBuilder {\n            _phantom: PhantomData,\n            positive: Some(Setter::Value(value)),\n            zero: self.zero,\n        }\n    }\n}\n\nimpl\u003cT1\u003e TestBuilder\u003cT1, ()\u003e {\n    /// # zero\n    /// - Type: `i32`\n    /// - Default: `0`\n    ///\n    /// An integer having zero as a default value.\n    pub fn zero(self, value: i32) -\u003e TestBuilder\u003cT1, i32\u003e {\n        TestBuilder {\n            _phantom: PhantomData,\n            positive: self.positive,\n            zero: Some(Setter::Value(value)),\n        }\n    }\n}\n```\n\n## How it works\n\nThe following code\n\n```rust\n#[derive(Builder)]\nstruct Person {\n    #[into]\n    #[validator(is_not_empty)]\n    name: String,\n    age: i32,\n    #[default(Gender::Nonbinary)]\n    gender: Gender,\n}\n\n```\n\nwill generates:\n\n```rust\nimpl Person {\n    // Create an empty builder\n    fn new\u003c'a\u003e() -\u003e PersonBuilder\u003c'a, (), (), (), (), ()\u003e {\n        PersonBuilder {\n            _phantom: PhantomData,\n            age: None,\n            name: None,\n            gender: Some(Setter::Value(Gender::Nonbinary)),\n        }\n    }\n}\n// A builder structure for `Person`.\nstruct PersonBuilder\u003c\n    'a, T1, T2, T3,\n    AsyncFieldMarker, // A generic for checking async fields\n    ValidatorOption,  // A generic for checking lazy validators\n\u003e {\n    _phantom: PhantomData\u003c(\n        T1, T2, T3,\n        AsyncFieldMarker,\n        ValidatorOption,\n    )\u003e,\n    // Fields are wrapped in `Option`s.\n    age: Option\u003cSetter\u003c'a, i32\u003e\u003e,\n    name: Option\u003cSetter\u003c'a, String\u003e\u003e,\n    gender: Option\u003cSetter\u003c'a, Gender\u003e\u003e,\n}\n// Implementation for `build` function\nimpl\u003c'a, T3\u003e\n    // It can be called regardless of whether `T3` is `()` or `Gender`.\n    PersonBuilder\u003c'a, i32, String, T3, (), ()\u003e\n{\n    fn build(self) -\u003e Person {\n        let age = match self.age.unwrap() {\n            Setter::Value(v) =\u003e v,\n            Setter::Lazy(f) =\u003e f(),\n            _ =\u003e unimplemented!(),\n        };\n        let name = match self.name.unwrap() {\n            Setter::Value(v) =\u003e v,\n            Setter::Lazy(f) =\u003e f(),\n            _ =\u003e unimplemented!(),\n        };\n        let gender = match self.gender.unwrap() {\n            Setter::Value(v) =\u003e v,\n            Setter::Lazy(f) =\u003e f(),\n            _ =\u003e unimplemented!(),\n        };\n        Person { age, name, gender }\n    }\n}\nimpl\u003c'a, T2, T3, AsyncFieldMarker, ValidatorOption\u003e\n    PersonBuilder\u003c\n        'a, (), T2, T3,\n        AsyncFieldMarker,\n        ValidatorOption,\n    \u003e\n{\n    // Setter for `age`\n    fn age(\n        self,\n        value: i32,\n    ) -\u003e PersonBuilder\u003c\n        'a, i32, T2, T3,\n        AsyncFieldMarker,\n        ValidatorOption,\n    \u003e {\n        PersonBuilder {\n            _phantom: PhantomData,\n            age: Some(Setter::Value(value.into())),\n            name: self.name,\n            gender: self.gender,\n        }\n    }\n}\nimpl\u003c'a, T1, T3, AsyncFieldMarker, ValidatorOption\u003e\n    PersonBuilder\u003c\n        'a, T1, (), T3,\n        AsyncFieldMarker,\n        ValidatorOption,\n    \u003e\n{\n    // Setter for `name`\n    fn name\u003cIntoType: Into\u003cString\u003e\u003e(\n        self,\n        value: IntoType,\n    ) -\u003e Result\u003c\n        PersonBuilder\u003c\n            'a, T1, String, T3,\n            AsyncFieldMarker,\n            ValidatorOption,\n        \u003e,\n        String,\n    \u003e {\n        // Validate the value\n        match is_not_empty(value.into()) {\n            Ok(value) =\u003e Ok(PersonBuilder {\n                _phantom: PhantomData,\n                age: self.age,\n                name: Some(Setter::Value(value)),\n                gender: self.gender,\n            }),\n            Err(e) =\u003e Err(format!(\"Validation failed: {:?}\", e)),\n        }\n    }\n}\nimpl\u003c'a, T1, T2, AsyncFieldMarker, ValidatorOption\u003e\n    PersonBuilder\u003c\n        'a, T1, T2, (),\n        AsyncFieldMarker,\n        ValidatorOption,\n    \u003e\n{\n    // Setter for `gender`\n    fn gender(\n        self,\n        value: Gender,\n    ) -\u003e PersonBuilder\u003c\n        'a, T1, T2, Gender,\n        AsyncFieldMarker,\n        ValidatorOption,\n    \u003e {\n        PersonBuilder {\n            _phantom: PhantomData,\n            age: self.age,\n            name: self.name,\n            gender: Some(Setter::Value(value.into())),\n        }\n    }\n}\n```\n\n## License\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseokminhong%2Fbuilder-pattern","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseokminhong%2Fbuilder-pattern","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseokminhong%2Fbuilder-pattern/lists"}