{"id":30959250,"url":"https://github.com/tomoikey/refined_type","last_synced_at":"2025-09-11T16:29:18.955Z","repository":{"id":206700382,"uuid":"717388272","full_name":"tomoikey/refined_type","owner":"tomoikey","description":"`refined_type` is a library that facilitates type composition, enabling the simple description of efficient validation processes and high runtime safety.","archived":false,"fork":false,"pushed_at":"2025-08-11T13:01:00.000Z","size":346,"stargazers_count":75,"open_issues_count":3,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-16T16:29:10.722Z","etag":null,"topics":["crate","crates-io","library","refined-types","refinement-types","runtime-validation","rust","rust-lang","safety","static-typing","type-library","validation"],"latest_commit_sha":null,"homepage":"https://docs.tomoikey.com/introduction","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/tomoikey.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":"CODE_OF_CONDUCT.md","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}},"created_at":"2023-11-11T10:38:27.000Z","updated_at":"2025-06-18T12:10:55.000Z","dependencies_parsed_at":"2024-02-20T18:44:58.264Z","dependency_job_id":"05451c9b-c6c4-4fd8-809c-cef5e1ef6dd2","html_url":"https://github.com/tomoikey/refined_type","commit_stats":null,"previous_names":["tomoikey/refined","tomoikey/refined-type","tomoikey/refined_type"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tomoikey/refined_type","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomoikey%2Frefined_type","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomoikey%2Frefined_type/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomoikey%2Frefined_type/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomoikey%2Frefined_type/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tomoikey","download_url":"https://codeload.github.com/tomoikey/refined_type/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomoikey%2Frefined_type/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274669088,"owners_count":25327991,"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","status":"online","status_checked_at":"2025-09-11T02:00:13.660Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["crate","crates-io","library","refined-types","refinement-types","runtime-validation","rust","rust-lang","safety","static-typing","type-library","validation"],"created_at":"2025-09-11T16:29:14.007Z","updated_at":"2025-09-11T16:29:18.948Z","avatar_url":"https://github.com/tomoikey.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch2 align=\"center\"\u003eRefined Type\u003c/h2\u003e\n\n\u003cdiv align=\"center\"\u003e\n    \u003cdiv\u003e\n        \u003cimg src=\"https://img.shields.io/crates/v/refined_type.svg\"/\u003e\n        \u003cimg src=\"https://img.shields.io/crates/d/refined_type\"/\u003e\n    \u003c/div\u003e\n    \u003ci\u003eCode More simply, More safely, for all Rustaceans.🦀\u003c/i\u003e\n    \u003cbr/\u003e\n    \u003cimg width=550 src=\"https://github.com/user-attachments/assets/2ae4bfee-1d42-4ed7-820a-b13260d359ef\"\u003e\n    \u003cbr/\u003e\n    \u003cdiv\u003e\n        \u003ca href=\"https://github.com/tomoikey/refined_type/stargazers\"\u003e\n            \u003cimg src=\"https://img.shields.io/github/stars/tomoikey/refined_type\" alt=\"Stars Badge\"/\u003e\n        \u003c/a\u003e\n        \u003ca href=\"https://github.com/tomoikey/refined_type/network/members\"\u003e\n            \u003cimg src=\"https://img.shields.io/github/forks/tomoikey/refined_type\" alt=\"Forks Badge\"/\u003e\n        \u003c/a\u003e\n    \u003c/div\u003e\n    \u003ca href=\"https://github.com/tomoikey/refined_type/pulls\"\u003e\n        \u003cimg src=\"https://img.shields.io/github/issues-pr/tomoikey/refined_type\" alt=\"Pull Requests Badge\"/\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/tomoikey/refined_type/issues\"\u003e\n        \u003cimg src=\"https://img.shields.io/github/issues/tomoikey/refined_type\" alt=\"Issues Badge\"/\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/tomoikey/refined_type/graphs/contributors\"\u003e\n        \u003cimg alt=\"GitHub contributors\" src=\"https://img.shields.io/github/contributors/tomoikey/refined_type?color=2b9348\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/tomoikey/refined_type/blob/main/LICENSE\"\u003e\n        \u003cimg src=\"https://img.shields.io/github/license/tomoikey/refined_type?color=2b9348\" alt=\"License Badge\"/\u003e\n    \u003c/a\u003e\n\u003c/div\u003e\n\n---\n\n**refined_type** is a library developed for Rust. It enhances your types, making them more robust and expanding the\nrange of guarantees your applications can statically ensure.\n\nYou can create various rules for a certain type, such as phone numbers, addresses, times, and so on.\nOnce you have established the rules, you can easily combine them.\nSpecifically, if you create rules for 'non-empty strings' and 'strings composed only of alphabets,' you do not need to\nredefine a new rule for 'non-empty strings composed only of alphabets'.\nAll rules can be arbitrarily combined and extended as long as the target type matches. Enjoy a wonderful type life!\n\n# Installation\n\n```shell\ncargo add refined_type\n```\n\n# Get Started\n\nAs an example, let's convert from JSON to a struct.\n\n```rust\n// define a struct for converting from JSON.\n#[derive(Debug, Deserialize)]\nstruct Human {\n    name: NonEmptyString,\n    age: MinMaxU8\u003c18, 80\u003e,\n    friends: NonEmptyVec\u003cString\u003e,\n}\n\n// In the 1st example, all fields satisfy the rule, causing the conversion from JSON to succeed.\nfn get_started_simple_example() -\u003e anyhow::Result\u003c()\u003e {\n    let json = json! {{\n        \"name\": \"john\",\n        \"age\": 20,\n        \"friends\": [\"tom\", \"taro\"]\n    }}\n        .to_string();\n\n    let human = serde_json::from_str::\u003cHuman\u003e(\u0026json)?;\n\n    assert_eq!(human.name.into_value(), \"john\");\n    assert_eq!(human.age.into_value(), 20);\n    assert_eq!(human.friends.into_value(), vec![\"tom\", \"taro\"]);\n    Ok(())\n}\n\n// In the 2nd example, while `name` does not satisfy the rule, `age` and `friends` do, causing the conversion from JSON to fail.\nfn get_started_empty_name_example() -\u003e anyhow::Result\u003c()\u003e {\n    let json = json! {{\n        \"name\": \"\",\n        \"age\": 20,\n        \"friends\": [\"tom\", \"taro\"]\n    }}\n        .to_string();\n\n    // because `name` is empty\n    assert!(serde_json::from_str::\u003cHuman\u003e(\u0026json).is_err());\n    Ok(())\n}\n\n// In the 3rd example, while `age` does not satisfy the rule, `name` and `friends` do, causing the conversion from JSON to fail.\nfn get_started_outbound_age_example() -\u003e anyhow::Result\u003c()\u003e {\n    let json = json! {{\n        \"name\": \"john\",\n        \"age\": 100,\n        \"friends\": [\"tom\", \"taro\"]\n    }}\n        .to_string();\n\n    // because `age` is not in the range of 18 to 80\n    assert!(serde_json::from_str::\u003cHuman\u003e(\u0026json).is_err());\n    Ok(())\n}\n\n// In the 4th example, while `friends` does not satisfy the rule, `name` and `age` do, causing the conversion from JSON to fail.\nfn get_started_empty_vec_example() -\u003e anyhow::Result\u003c()\u003e {\n    let json = json! {{\n        \"name\": \"john\",\n        \"age\": 20,\n        \"friends\": []\n    }}\n        .to_string();\n\n    // because `friends` is empty\n    assert!(serde_json::from_str::\u003cHuman\u003e(\u0026json).is_err());\n    Ok(())\n}\n```\n\n---\n\n# Compose Rules\n\nAs mentioned earlier, it is possible to combine any rules as long as the target types match.\nIn the example below, there are standalone rules for 'strings containing Hello' and 'strings containing World'.\nSince their target type is String, combining them is possible.\nI have prepared something called Rule Composer (`And`, `Or`, `Not`).\nBy using Rule Composer, composite rules can be easily created.\n\n### 1: `And` Rule Composer\n\n`And` Rule Composer is a rule that satisfies both of the two rules.\nIt is generally effective when you want to narrow down the condition range.\n\n```rust\ntype Target = Refined\u003cAnd![EvenRuleU8, MinMaxRuleU8\u003c0, 100\u003e]\u003e;\n```\n\n```rust\nfn and_example() -\u003e Result\u003c(), Error\u003cu8\u003e\u003e {\n    let target = Target::new(50)?;\n    assert_eq!(target.into_value(), 50);\n\n    let target = Target::new(51);\n    assert!(target.is_err());\n\n    Ok(())\n}\n```\n\n### 2: `Or` Rule Composer\n\n`Or` Rule Composer is a rule that satisfies either of the two rules.\nIt is generally effective when you want to expand the condition range.\n\n```rust\ntype Target = Refined\u003cOr![LessRuleU8\u003c10\u003e, GreaterRuleU8\u003c50\u003e]\u003e;\n```\n\n```rust\nfn or_example() -\u003e Result\u003c(), Error\u003cu8\u003e\u003e {\n    let target = Target::new(5)?;\n    assert_eq!(target.into_value(), 5);\n\n    let target = Target::new(10);\n    assert!(target.is_err());\n\n    let target = Target::new(50);\n    assert!(target.is_err());\n\n    let target = Target::new(51)?;\n    assert_eq!(target.into_value(), 51);\n\n    Ok(())\n}\n```\n\n### 3: `Not` Rule Composer\n\n`Not` Rule Composer is a rule that does not satisfy a specific condition.\nIt is generally effective when you want to discard only certain situations.\n\n```rust\ntype Target = Refined\u003cNot\u003cEqualRuleU8\u003c50\u003e\u003e\u003e;\n```\n\n```rust\nfn not_example() -\u003e Result\u003c(), Error\u003cu8\u003e\u003e {\n    let target = Target::new(49)?;\n    assert_eq!(target.into_value(), 49);\n\n    let target = Target::new(50);\n    assert!(target.is_err());\n\n    let target = Target::new(51)?;\n    assert_eq!(target.into_value(), 51);\n\n    Ok(())\n}\n```\n\n### 4: `If` Rule Composer\n\n`If` Rule Composer is a rule that applies a specific rule only when a certain condition is met.\n\n```rust\ntype Target = Refined\u003cIf\u003cGreaterEqualRuleI8\u003c10\u003e, EvenRuleI8\u003e\u003e;\n\nfn if_example() -\u003e Result\u003c(), Error\u003ci8\u003e\u003e {\n    let target = Target::new(8)?;\n    assert_eq!(target.into_value(), 8);\n\n    let target = Target::new(9)?;\n    assert_eq!(target.into_value(), 9);\n\n    let target = Target::new(10)?;\n    assert_eq!(target.into_value(), 10);\n\n    let target = Target::new(11);\n    assert!(target.is_err());\n\n    Ok(())\n}\n```\n\n### 5: `IfElse` Rule Composer\n\n`IfElse` Rule Composer is a rule that applies a specific rule when a certain condition is met and another rule when it\nis not met.\n\n```rust\ntype Target = Refined\u003cIfElse\u003cGreaterEqualRuleI8\u003c10\u003e, EvenRuleI8, OddRuleI8\u003e\u003e;\n\nfn if_else_example() -\u003e Result\u003c(), Error\u003ci8\u003e\u003e {\n    let target = Target::new(8);\n    assert!(target.is_err());\n\n    let target = Target::new(9)?;\n    assert_eq!(target.into_value(), 9);\n\n    let target = Target::new(10)?;\n    assert_eq!(target.into_value(), 10);\n\n    let target = Target::new(11);\n    assert!(target.is_err());\n\n    Ok(())\n}\n```\n\n### 6: Other Rule Composer\n\n`Equiv`, `Nand`, `Nor` and `Xor` are also available.\n\n# Number\n\n## `MinMax`\n\n`MinMax` is a type that signifies the target exists between a certain number and another number.\n\n```rust\ntype Age = MinMaxU8\u003c18, 80\u003e;\n\nfn min_max_example() -\u003e Result\u003c(), Error\u003cu8\u003e\u003e {\n    let age = Age::new(18)?;\n    assert_eq!(age.into_value(), 18);\n\n    let age = Age::new(80)?;\n    assert_eq!(age.into_value(), 80);\n\n    let age = Age::new(17);\n    assert!(age.is_err());\n\n    let age = Age::new(81);\n    assert!(age.is_err());\n    Ok(())\n}\n```\n\n## `Less`\n\n`Less` is a type that signifies the target is less than a certain number.\n\n```rust\ntype Age = LessU8\u003c80\u003e;\n\nfn less_example() -\u003e Result\u003c(), Error\u003cu8\u003e\u003e {\n    let age = Age::new(79)?;\n    assert_eq!(age.into_value(), 79);\n\n    let age = Age::new(80);\n    assert!(age.is_err());\n\n    Ok(())\n}\n```\n\n## `Greater`\n\n`Greater` is a type that signifies the target is greater than a certain number.\n\n```rust\ntype Age = GreaterU8\u003c18\u003e;\n\nfn greater_example() -\u003e Result\u003c(), Error\u003cu8\u003e\u003e {\n    let age = Age::new(19)?;\n    assert_eq!(age.into_value(), 19);\n\n    let age = Age::new(18);\n    assert!(age.is_err());\n\n    Ok(())\n}\n```\n\n## `Equal`\n\n`Equal` is a type that signifies the target is equal to a certain number.\n\n```rust\ntype Age = EqualU8\u003c18\u003e;\n\nfn equal_example() -\u003e Result\u003c(), Error\u003cu8\u003e\u003e {\n    let age = Age::new(18)?;\n    assert_eq!(age.into_value(), 18);\n\n    let age = Age::new(19);\n    assert!(age.is_err());\n\n    Ok(())\n}\n```\n\n## `LessEqual`\n\n`LessEqual` is a type that signifies the target is less than or equal to a certain number.\n\n```rust\ntype Age = LessEqualU8\u003c80\u003e;\n\nfn less_equal_example() -\u003e Result\u003c(), Error\u003cu8\u003e\u003e {\n    let age = Age::new(79)?;\n    assert_eq!(age.into_value(), 79);\n\n    let age = Age::new(80)?;\n    assert_eq!(age.into_value(), 80);\n\n    let age = Age::new(81);\n    assert!(age.is_err());\n\n    Ok(())\n}\n```\n\n## `GreaterEqual`\n\n`GreaterEqual` is a type that signifies the target is greater than or equal to a certain number.\n\n```rust\ntype Age = GreaterEqualU8\u003c18\u003e;\n\nfn greater_equal_example() -\u003e Result\u003c(), Error\u003cu8\u003e\u003e {\n    let age = Age::new(19)?;\n    assert_eq!(age.into_value(), 19);\n\n    let age = Age::new(18)?;\n    assert_eq!(age.into_value(), 18);\n\n    let age = Age::new(17);\n    assert!(age.is_err());\n\n    Ok(())\n}\n```\n\n## `Range`\n\n`Range` is a type that signifies the target exists between a certain number and another number.\n\n```rust\ntype Age = RangeU8\u003c18, 80\u003e;\n\nfn range_example() -\u003e Result\u003c(), Error\u003cu8\u003e\u003e {\n    let age = Age::new(17);\n    assert!(age.is_err());\n\n    let age = Age::new(18)?;\n    assert_eq!(age.into_value(), 18);\n\n    let age = Age::new(79)?;\n    assert_eq!(age.into_value(), 79);\n\n    let age = Age::new(80);\n    assert!(age.is_err());\n\n    Ok(())\n}\n```\n\n# Iterator\n\n`refined_type` has several useful refined types for Iterators.\n\n## `ForAll`\n\n`ForAll` is a rule that applies a specific rule to all elements in the Iterator.\n\n```rust\nfn example_11() -\u003e anyhow::Result\u003c()\u003e {\n    let vec = vec![\"Hello\".to_string(), \"World\".to_string()];\n    let for_all_ok = ForAllVec::\u003cNonEmptyStringRule\u003e::new(vec.clone())?;\n    assert_eq!(vec, for_all_ok.into_value());\n\n    let vec = vec![\"Hello\".to_string(), \"\".to_string()];\n    let for_all_err = ForAllVec::\u003cNonEmptyStringRule\u003e::new(vec.clone());\n    assert!(for_all_err.is_err());\n    Ok(())\n}\n```\n\n## `Exists`\n\n`Exists` is a rule that applies a specific rule to at least one element in the Iterator.\n\n```rust\nfn example_12() -\u003e anyhow::Result\u003c()\u003e {\n    let vec = vec![\"Hello\".to_string(), \"\".to_string()];\n    let exists_ok = ExistsVec::\u003cNonEmptyStringRule\u003e::new(vec.clone())?;\n    assert_eq!(vec, exists_ok.into_value());\n\n    let vec = vec![\"\".to_string(), \"\".to_string()];\n    let exists_err = ExistsVec::\u003cNonEmptyStringRule\u003e::new(vec.clone());\n    assert!(exists_err.is_err());\n    Ok(())\n}\n```\n\n## `Head`\n\n`Head` is a rule that applies a specific rule to the first element in the Iterator.\n\n```rust\nfn example_13() -\u003e anyhow::Result\u003c()\u003e {\n    let table = vec![\n        (vec![\"good morning\".to_string(), \"\".to_string()], true), // PASS\n        (vec![\"hello\".to_string(), \"hello\".to_string()], true),   // PASS\n        (vec![], false),                                          // FAIL\n        (vec![\"\".to_string()], false),                            // FAIL\n        (vec![\"\".to_string(), \"hello\".to_string()], false),       // FAIL\n    ];\n\n    for (value, ok) in table {\n        let head = HeadVec::\u003cNonEmptyStringRule\u003e::new(value.clone());\n        assert_eq!(head.is_ok(), ok);\n    }\n\n    Ok(())\n}\n```\n\n## `Last`\n\n`Last` is a rule that applies a specific rule to the last element in the Iterator.\n\n```rust\nfn example_14() -\u003e anyhow::Result\u003c()\u003e {\n    let table = vec![\n        (vec![\"\".to_string(), \"hello\".to_string()], true), // PASS\n        (vec![\"good morning\".to_string(), \"hello\".to_string()], true), // PASS\n        (vec![], false),                                   // FAIL\n        (vec![\"\".to_string()], false),                     // FAIL\n        (vec![\"hello\".to_string(), \"\".to_string()], false), // FAIL\n    ];\n\n    for (value, ok) in table {\n        let last = LastVec::\u003cNonEmptyStringRule\u003e::new(value.clone());\n        assert_eq!(last.is_ok(), ok);\n    }\n\n    Ok(())\n}\n```\n\n## `Tail`\n\n`Tail` is a rule that applies a specific rule to all elements except the first element in the Iterator.\n\n```rust\nfn example_15() -\u003e anyhow::Result\u003c()\u003e {\n    let table = vec![\n        (vec![\"hey\".to_string(), \"hello\".to_string(), \"world\".to_string()], true),\n        (vec![\"hey\".to_string(), \"hello\".to_string(), \"\".to_string()], false),\n        (vec![\"hey\".to_string(), \"\".to_string(), \"world\".to_string()], false),\n        (vec![\"hey\".to_string(), \"\".to_string(), \"\".to_string()], false),\n        (vec![\"\".to_string(), \"hello\".to_string(), \"world\".to_string()], true),\n        (vec![\"\".to_string(), \"hello\".to_string(), \"\".to_string()], false),\n        (vec![\"\".to_string(), \"\".to_string(), \"world\".to_string()], false),\n        (vec![\"\".to_string(), \"\".to_string(), \"\".to_string()], false),\n    ];\n\n    for (value, ok) in table {\n        let tail = TailVec::\u003cNonEmptyStringRule\u003e::new(value.clone());\n        assert_eq!(tail.is_ok(), ok);\n    }\n\n    Ok(())\n}\n```\n\n## `Init`\n\n`Init` is a rule that applies a specific rule to all elements except the last element in the Iterator.\n\n```rust\nfn example_16() -\u003e anyhow::Result\u003c()\u003e {\n    let table = vec![\n        (vec![\"hey\".to_string(), \"hello\".to_string(), \"world\".to_string()], true),\n        (vec![\"hey\".to_string(), \"hello\".to_string(), \"\".to_string()], true),\n        (vec![\"hey\".to_string(), \"\".to_string(), \"world\".to_string()], false),\n        (vec![\"hey\".to_string(), \"\".to_string(), \"\".to_string()], false),\n        (vec![\"\".to_string(), \"hello\".to_string(), \"world\".to_string()], false),\n        (vec![\"\".to_string(), \"hello\".to_string(), \"\".to_string()], false),\n        (vec![\"\".to_string(), \"\".to_string(), \"world\".to_string()], false),\n        (vec![\"\".to_string(), \"\".to_string(), \"\".to_string()], false),\n    ];\n\n    for (value, ok) in table {\n        let init = InitVec::\u003cNonEmptyStringRule\u003e::new(value.clone());\n        assert_eq!(init.is_ok(), ok);\n    }\n\n    Ok(())\n}\n```\n\n## `Index`\n\n`Index` is a rule that applies a specific rule to the element at a specific index in the Iterator.\n\n```rust\nfn example_17() -\u003e anyhow::Result\u003c()\u003e {\n    let table = vec![\n        (vec![\"good morning\".to_string(), \"hello\".to_string()], true),\n        (vec![\"good morning\".to_string(), \"\".to_string()], false),\n        (vec![\"\".to_string(), \"hello\".to_string()], true),\n        (vec![\"\".to_string(), \"\".to_string()], false),\n    ];\n\n    for (value, expected) in table {\n        let refined = IndexVec::\u003c1, NonEmptyStringRule\u003e::new(value.clone());\n        assert_eq!(refined.is_ok(), expected);\n    }\n\n    Ok(())\n}\n```\n\n## `CountEqual`\n\n`CountEqual` is a type that signifies the number of elements that satisfy the rule is a specific number.\n\n```rust\nfn count_equal_example() -\u003e Result\u003c(), Error\u003cVec\u003ci32\u003e\u003e\u003e {\n    let table = vec![\n        (vec![\"good morning\".to_string(), \"hello\".to_string()], false),\n        (vec![\"good morning\".to_string(), \"\".to_string()], true),\n        (vec![\"\".to_string(), \"hello\".to_string()], true),\n        (vec![\"\".to_string(), \"\".to_string()], false),\n    ];\n\n    for (value, expected) in table {\n        let refined = CountEqualVec::\u003c1, NonEmptyStringRule\u003e::new(value.clone());\n        assert_eq!(refined.is_ok(), expected);\n    }\n\n    Ok(())\n}\n```\n\n## `CountLess`\n\n`CountLess` is a type that signifies the number of elements that satisfy the rule is less than a specific number.\n\n```rust\nfn count_less_example() -\u003e Result\u003c(), Error\u003cVec\u003ci32\u003e\u003e\u003e {\n    let table = vec![\n        (vec![\"good morning\".to_string(), \"hello\".to_string()], false),\n        (vec![\"good morning\".to_string(), \"\".to_string()], true),\n        (vec![\"\".to_string(), \"hello\".to_string()], true),\n        (vec![\"\".to_string(), \"\".to_string()], true),\n    ];\n\n    for (value, expected) in table {\n        let refined = CountLessVec::\u003c2, NonEmptyStringRule\u003e::new(value.clone());\n        assert_eq!(refined.is_ok(), expected);\n    }\n\n    Ok(())\n}\n```\n\n## `CountGreater`\n\n`CountGreater` is a type that signifies the number of elements that satisfy the rule is greater than a specific number.\n\n```rust\nfn count_greater_example() -\u003e Result\u003c(), Error\u003cVec\u003ci32\u003e\u003e\u003e {\n    let table = vec![\n        (vec![\"good morning\".to_string(), \"hello\".to_string()], true),\n        (vec![\"good morning\".to_string(), \"\".to_string()], false),\n        (vec![\"\".to_string(), \"hello\".to_string()], false),\n        (vec![\"\".to_string(), \"\".to_string()], false),\n    ];\n\n    for (value, expected) in table {\n        let refined = CountGreaterVec::\u003c1, NonEmptyStringRule\u003e::new(value.clone());\n        assert_eq!(refined.is_ok(), expected);\n    }\n\n    Ok(())\n}\n```\n\n## `CountLessEqual`\n\n`CountLessEqual` is a type that signifies the number of elements that satisfy the rule is less than or equal to a\nspecific number.\n\n```rust\nfn count_less_equal_example() -\u003e Result\u003c(), Error\u003cVec\u003ci32\u003e\u003e\u003e {\n    let table = vec![\n        (vec![\"good morning\".to_string(), \"hello\".to_string()], true),\n        (vec![\"good morning\".to_string(), \"\".to_string()], true),\n        (vec![\"\".to_string(), \"hello\".to_string()], true),\n        (vec![\"\".to_string(), \"\".to_string()], true),\n    ];\n\n    for (value, expected) in table {\n        let refined = CountLessEqualVec::\u003c2, NonEmptyStringRule\u003e::new(value.clone());\n        assert_eq!(refined.is_ok(), expected);\n    }\n\n    Ok(())\n}\n```\n\n## `CountGreaterEqual`\n\n`CountGreaterEqual` is a type that signifies the number of elements that satisfy the rule is greater than or equal to a\nspecific number.\n\n```rust\nfn count_greater_equal_example() -\u003e Result\u003c(), Error\u003cVec\u003ci32\u003e\u003e\u003e {\n    let table = vec![\n        (vec![\"good morning\".to_string(), \"hello\".to_string()], true),\n        (vec![\"good morning\".to_string(), \"\".to_string()], true),\n        (vec![\"\".to_string(), \"hello\".to_string()], true),\n        (vec![\"\".to_string(), \"\".to_string()], false),\n    ];\n\n    for (value, expected) in table {\n        let refined = CountGreaterEqualVec::\u003c1, NonEmptyStringRule\u003e::new(value.clone());\n        assert_eq!(refined.is_ok(), expected);\n    }\n\n    Ok(())\n}\n```\n\n## `Reverse`\n\n`Reverse` is a rule that applies a specific rule to all elements in the Iterator in reverse order.\n\n```rust\nfn example_18() -\u003e Result\u003c(), Error\u003cVec\u003ci32\u003e\u003e\u003e {\n    let table = vec![\n        (vec![\"good morning\".to_string(), \"hello\".to_string()], true),\n        (vec![\"good morning\".to_string(), \"\".to_string()], false),\n        (vec![\"\".to_string(), \"hello\".to_string()], true),\n        (vec![\"\".to_string(), \"\".to_string()], false),\n    ];\n\n    for (value, expected) in table {\n        let refined = Reverse::\u003cIndexRuleVec\u003c0, NonEmptyStringRule\u003e\u003e::new(value.clone());\n        assert_eq!(refined.is_ok(), expected);\n    }\n\n    Ok(())\n}\n```\n\n## `Skip`\n\n`Skip` is a rule that applies a specific rule to the elements of the Iterator while skipping the elements according\nto `SkipOption`.\n\n```rust\nfn example_19() -\u003e Result\u003c(), Error\u003cVec\u003ci32\u003e\u003e\u003e {\n    let table = vec![\n        (vec![\"hey\".to_string(), \"hello\".to_string(), \"world\".to_string()], true),\n        (vec![\"hey\".to_string(), \"hello\".to_string(), \"\".to_string()], false),\n        (vec![\"hey\".to_string(), \"\".to_string(), \"world\".to_string()], false),\n        (vec![\"hey\".to_string(), \"\".to_string(), \"\".to_string()], false),\n        (vec![\"\".to_string(), \"hello\".to_string(), \"world\".to_string()], true),\n        (vec![\"\".to_string(), \"hello\".to_string(), \"\".to_string()], false),\n        (vec![\"\".to_string(), \"\".to_string(), \"world\".to_string()], false),\n        (vec![\"\".to_string(), \"\".to_string(), \"\".to_string()], false),\n    ];\n\n    for (value, ok) in table {\n        let init = SkipVec::\u003cNonEmptyStringRule, SkipFirst\u003c_\u003e\u003e::new(value.clone());\n        assert_eq!(init.is_ok(), ok);\n    }\n\n    Ok(())\n}\n```\n\nif you need more skip option, you can define it like this.\n\n```rust\npub struct NoSkip\u003cT\u003e {\n    _phantom_data: std::marker::PhantomData\u003cT\u003e,\n}\n\nimpl\u003cITEM\u003e SkipOption for NoSkip\u003cITEM\u003e {\n    type Item = ITEM;\n    type Accumulator = ();\n    fn should_skip(_: usize, _: Option\u003c\u0026mut Self::Accumulator\u003e, _: \u0026Self::Item) -\u003e bool {\n        false\n    }\n}\n```\n\n# Length\n\nYou can impose constraints on objects that have a length, such as `String` or `Vec`.\n\n## `LengthMinMax`\n\n`LengthMinMax` is a type that signifies the target has a length between a certain number and another number.\n\n```rust\nfn length_min_max_example() -\u003e Result\u003c(), Error\u003cString\u003e\u003e {\n    type Password = LengthMinMax\u003c5, 10, String\u003e;\n\n    let password = Password::new(\"123456\".to_string())?;\n    assert_eq!(password.into_value(), \"123456\");\n\n    let password = Password::new(\"1234\".to_string());\n    assert!(password.is_err());\n\n    let password = Password::new(\"12345678901\".to_string());\n    assert!(password.is_err());\n\n    Ok(())\n}\n```\n\n## `LengthGreater`\n\n`LengthGreater` is a type that signifies the target has a length greater than a certain number.\n\n```rust\nfn length_greater_example() -\u003e Result\u003c(), Error\u003cString\u003e\u003e {\n    type Password = LengthGreater\u003c5, String\u003e;\n\n    let password = Password::new(\"123456\".to_string())?;\n    assert_eq!(password.into_value(), \"123456\");\n\n    let password = Password::new(\"1234\".to_string());\n    assert!(password.is_err());\n\n    Ok(())\n}\n```\n\n## `LengthLess`\n\n`LengthLess` is a type that signifies the target has a length less than a certain number.\n\n```rust\nfn length_less_example() -\u003e Result\u003c(), Error\u003cString\u003e\u003e {\n    type Password = LengthLess\u003c10, String\u003e;\n\n    let password = Password::new(\"123456\".to_string())?;\n    assert_eq!(password.into_value(), \"123456\");\n\n    let password = Password::new(\"12345678901\".to_string());\n    assert!(password.is_err());\n\n    Ok(())\n}\n```\n\n## `LengthEqual`\n\n`LengthEqual` is a type that signifies the target has a length equal to a certain number.\n\n```rust\nfn length_equal_example() -\u003e Result\u003c(), Error\u003cString\u003e\u003e {\n    type Password = LengthEqual\u003c5, String\u003e;\n\n    let password = Password::new(\"12345\".to_string())?;\n    assert_eq!(password.into_value(), \"12345\");\n\n    let password = Password::new(\"1234\".to_string());\n    assert!(password.is_err());\n\n    Ok(())\n}\n```\n\n## Custom Length\n\nYou can define a length for any type. Therefore, if you want to implement a length that is not provided\nby `refined_type`, you can easily do so using `LengthDefinition`.\n\n```rust\n#[derive(Debug, PartialEq)]\nstruct Hello;\nimpl LengthDefinition for Hello {\n    fn length(\u0026self) -\u003e usize {\n        5\n    }\n}\n\nfn custom_length_example() -\u003e Result\u003c(), Error\u003cHello\u003e\u003e {\n    let hello = Refined::\u003cLengthEqualRule\u003c5, Hello\u003e\u003e::new(Hello)?;\n    assert_eq!(hello.into_value(), Hello);\n    Ok(())\n}\n```\n\n# JSON\n\n`refined_type` is compatible with `serde_json`. This ensures type-safe communication and eliminates the need to write\nnew validation processes. All you need to do is implement a set of rules once and implement `serde`’s `Serialize`\nand `Deserialize`.\n\n### Serialize\n\n```rust\n#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]\nstruct Human2 {\n    name: NonEmptyString,\n    age: u8,\n}\n\nfn example_9() -\u003e anyhow::Result\u003c()\u003e {\n    let john = Human2 {\n        name: NonEmptyString::new(\"john\".to_string())?,\n        age: 8,\n    };\n\n    let actual = json!(john);\n    let expected = json! {{\n        \"name\": \"john\",\n        \"age\": 8\n    }};\n    assert_eq!(actual, expected);\n    Ok(())\n}\n```\n\n### Deserialize\n\n```rust\nfn example_10() -\u003e anyhow::Result\u003c()\u003e {\n    let json = json! {{\n        \"name\": \"john\",\n        \"age\": 8\n    }}\n        .to_string();\n\n    let actual = serde_json::from_str::\u003cHuman2\u003e(\u0026json)?;\n\n    let expected = Human2 {\n        name: NonEmptyString::new(\"john\".to_string())?,\n        age: 8,\n    };\n    assert_eq!(actual, expected);\n    Ok(())\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomoikey%2Frefined_type","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomoikey%2Frefined_type","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomoikey%2Frefined_type/lists"}