{"id":20982231,"url":"https://github.com/immmdreza/engineer","last_synced_at":"2025-05-14T16:31:25.990Z","repository":{"id":65123005,"uuid":"582271473","full_name":"immmdreza/engineer","owner":"immmdreza","description":"Engineer is a master builder based on Optional.","archived":false,"fork":false,"pushed_at":"2023-01-30T12:35:07.000Z","size":57,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-11-07T03:49:34.248Z","etag":null,"topics":["builder","codegen","crate","option","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/immmdreza.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-12-26T09:38:48.000Z","updated_at":"2023-03-12T20:15:03.000Z","dependencies_parsed_at":"2023-02-16T05:55:22.184Z","dependency_job_id":null,"html_url":"https://github.com/immmdreza/engineer","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/immmdreza%2Fengineer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/immmdreza%2Fengineer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/immmdreza%2Fengineer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/immmdreza%2Fengineer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/immmdreza","download_url":"https://codeload.github.com/immmdreza/engineer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225302977,"owners_count":17453028,"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","codegen","crate","option","rust"],"created_at":"2024-11-19T05:44:54.486Z","updated_at":"2024-11-19T05:44:55.046Z","avatar_url":"https://github.com/immmdreza.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Engineer \n\n[![Build and Test Rust](https://github.com/immmdreza/engineer/actions/workflows/build-and-test.yml/badge.svg?branch=master)](https://github.com/immmdreza/engineer/actions/workflows/build-and-test.yml)\n\nEngineer is a master builder based on `Option`.\n\nIt just generates an Engineer (Builder) class for a data model.\n\n## Installation\n\nAdd following as dependencies\n\n```toml\n[dependencies]\nengineer = \"0.1.6\"\n```\n\n## Get Started\n\n```rust\nuse engineer::Engineer;\n\n#[derive(Engineer)]\nstruct Identity {\n    id: usize,\n    username: String,\n    first_name: Option\u003cString\u003e,\n    last_name: Option\u003cString\u003e,\n    lang_code: Option\u003cString\u003e,\n}\n```\n\nOptional fields are not required during the initialization.\n\n```rust\n// Option fields are set to None.\nlet identity = Identity::engineer(0, \"immmdreza\".to_string()).done();\n```\n\nBut you can set a value for `Option` fields as well.\n\n```rust\nlet identity = Identity::engineer(0, \"immmdreza\".to_string()) // IdentityEngineer\n    .first_name(\"Arash\".to_string()) // IdentityEngineer\n    .last_name(\"Tofani\".to_string()) // IdentityEngineer\n    .done(); // Identity\n```\n\nThat's all for the basics, but you can do a little customizations.\n\n## Customizations\n\n### Engineer Struct Name\n\nEngineer struct name is {struct}Engineer (`IdentityEngineer` for `Identity`) by default, but you can change that.\n\n```rust\n// ~~~ sniff ~~~\n\n#[derive(Engineer)]\n#[engineer(engineer_name = \"IdentityBuilder\")]\nstruct Identity {\n    // ~~~ sniff ~~~\n}\n\n// ~~~ sniff ~~~\n\n    let identity = Identity::engineer(0, \"immmdreza\".to_string()) // IdentityBuilder\n        .first_name(\"Arash\".to_string()) // IdentityBuilder\n        .last_name(\"Tofani\".to_string()) // IdentityBuilder\n        .done(); // Identity\n```\n\n### Builder Function Name\n\nThe name of builder function is `engineer` by default, but guess what?\n\n```rust\n// ~~~ sniff ~~~\n\n#[derive(Engineer)]\n#[engineer(builder_func = \"builder\")]\nstruct Identity {\n    // ~~~ sniff ~~~\n}\n\n// ~~~ sniff ~~~\n\n    let identity = Identity::builder(0, \"immmdreza\".to_string())\n    // ~~~ sniff ~~~\n```\n\nYou want to use this as `new` function:\n\n```rust\n// ~~~ sniff ~~~\n\n#[derive(Engineer)]\n#[engineer(builder_func = \"new\")]\nstruct Identity {\n    // ~~~ sniff ~~~\n}\n\n// ~~~ sniff ~~~\n\n    let identity = Identity::new(0, \"immmdreza\".to_string())\n    // ~~~ sniff ~~~\n```\n\nThis can be simplified for special `new` name as builder function:\n\n```rust\n#[derive(Engineer)]\n#[engineer(new)]\nstruct Identity {\n    // ~~~ sniff ~~~\n}\n```\n\n### Default value for Options\n\nYou can set a default value for option fields.\n\nThis value is used if you don't set any other for them.\n\n```rust\n// ~~~ sniff ~~~\n\n#[derive(Engineer)]\n#[engineer(new)]\nstruct Identity {\n    // ~~~ sniff ~~~\n    #[engineer(default_value = r#\"String::from(\"fa\")\"#)]\n    lang_code: Option\u003cString\u003e,\n}\n\n// ~~~ sniff ~~~\n\n    let identity = Identity::new(0, \"immmdreza\".to_string());\n\n    identity.lang_code // Some(\"fa\")\n```\n\nAlternatively, you can use `default` to set `Some(Default::default)` instead of None if any other value is not given.\n\n```rust\n    // ~~~ sniff ~~~\n    #[engineer(default)]\n    luck_number: Option\u003ci32\u003e, // Some(0)\n    // ~~~ sniff ~~~\n```\n\n### Retype\n\nYou can change types requested in builder processes.\n\n```rust\n// ~~~ sniff ~~~\n\n#[derive(Engineer)]\n#[engineer(new)]\nstruct Identity {\n    // ~~~ sniff ~~~\n    #[engineer(retype(to = \"impl Into\u003cString\u003e\", re = \".into()\"))]\n    //                      ^                         ^\n    //                      | Requested type in public.\n    //                                                |\n    //                                                | How we recover to original type.\n    username: String,\n    // ~~~ sniff ~~~\n}\n\n// ~~~ sniff ~~~\n\n    let identity = Identity::new(0, \"immmdreza\"); // .to_string() is not needed.\n    // ~~~ sniff ~~~\n```\n\nAlternatively, for str retypes (like example above), you can use a shorthand `str_retype`.\n\n```rust\n    // ~~~ sniff ~~~\n    #[engineer(str_retype)]\n    username: String,\n    // ~~~ sniff ~~~\n```\n\nAlso you can use retypes globally.\n\n```rust\n#[derive(Engineer)]\n#[engineer(new, retype(from = \"String\", to = \"impl Into\u003cString\u003e\", re = \".into()\"))]\nstruct Identity {\n    // ~~~ sniff ~~~\n}\n```\n\nOr additionally for String retypes:\n\n```rust\n#[derive(Engineer)]\n#[engineer(new, str_retype)]\nstruct Identity {\n    // ~~~ sniff ~~~\n}\n```\n\nBoth codes above will retype **all** `String` fields into `impl Into\u003cString\u003e` in public api.\n\nFinal result\n\n```rust\n#[derive(Engineer)]\n#[engineer(new, str_retype)]\nstruct Identity {\n    id: usize,\n    username: String,\n    first_name: String,\n    last_name: Option\u003cString\u003e,\n    #[engineer(str_retype, default_value = \"\\\"fa\\\".to_string()\")]\n    lang_code: Option\u003cString\u003e,\n}\n\nfn print_identity(ident: impl Into\u003cIdentity\u003e) {\n    let ident: Identity = ident.into();\n    println!(\"{ident:#?}\");\n}\n\nfn main() {\n    let ident = Identity::new(1, \"immmdreza\", \"Arash\").last_name(\"Tofani\");\n\n    print_identity(ident);\n    // Identity {\n    //     id: 1,\n    //     username: \"immmdreza\",\n    //     first_name: \"Arash\",\n    //     last_name: Some(\n    //         \"Tofani\",\n    //     ),\n    //     lang_code: Some(\n    //         \"fa\",\n    //     ),\n    // }\n}\n```\n\n## More about traits\n\nThis crate has two main traits: `Builder\u003cT\u003e where T: Engineer` and `Engineer`.\n\n`Engineer` trait has two associated types: `Params` and `Builder`.\n\n- `Params` is a tuple of your required fields types ( fields that are not `Option\u003c_\u003e` )\n- `Builder` is the type of Builder/Engineer class.\n\nUsing `Engineer` macro, will make your data class implements `Engineer` trait and also\ncreates a Builder struct ( usually named `{struct_name}Engineer` - _i'm thinking about rename_ )\nthat implements `Builder\u003cT\u003e` where T is your own struct.\n\nThis enables you to do some generic stuff around these traits, As instance:\n\n```rust\nfn build_any\u003cE\u003e(required: E::Params) -\u003e E\nwhere\n    E: Engineer,\n{\n    E::build(required)\n}\n\n// Or\n\nfn get_builder\u003cE\u003e(required: E::Params) -\u003e E::Builder\nwhere\n    E: Engineer,\n{\n    E::builder(required)\n}\n```\n\n_Note that `E::Params` is a tuple._\n\nIf all of you required fields (E::Param) implement Default, another function `build_default`\nwill become available on you struct that creates a default instance with no inputs required.\n\nAdditionally, `Engineer` trait has two const fields:\n\n```rust\n- const NORMAL_FIELDS: usize;\n- const OPTIONAL_FIELDS: usize;\n```\n\n🧀\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimmmdreza%2Fengineer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fimmmdreza%2Fengineer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimmmdreza%2Fengineer/lists"}