{"id":23853651,"url":"https://github.com/matteopolak/wary","last_synced_at":"2025-04-14T02:27:56.182Z","repository":{"id":270526171,"uuid":"834896638","full_name":"matteopolak/wary","owner":"matteopolak","description":"A no_std-compatible validation and transformation library.","archived":false,"fork":false,"pushed_at":"2025-01-07T03:23:38.000Z","size":181,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-14T02:27:52.281Z","etag":null,"topics":["no-alloc","no-std","rust","transform","validate"],"latest_commit_sha":null,"homepage":"","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/matteopolak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","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":"2024-07-28T17:22:00.000Z","updated_at":"2025-01-07T03:23:40.000Z","dependencies_parsed_at":"2025-01-01T01:06:54.555Z","dependency_job_id":"0fc8df26-94f9-4f6b-a50e-e233779a0b2d","html_url":"https://github.com/matteopolak/wary","commit_stats":null,"previous_names":["matteopolak/wary"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matteopolak%2Fwary","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matteopolak%2Fwary/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matteopolak%2Fwary/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matteopolak%2Fwary/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/matteopolak","download_url":"https://codeload.github.com/matteopolak/wary/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248810734,"owners_count":21165171,"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":["no-alloc","no-std","rust","transform","validate"],"created_at":"2025-01-02T23:18:41.693Z","updated_at":"2025-04-14T02:27:56.140Z","avatar_url":"https://github.com/matteopolak.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Wary\n\n[![\u003chttps://img.shields.io/crates/v/wary\u003e](https://img.shields.io/crates/v/wary)](https://crates.io/crates/wary)\n[![\u003chttps://img.shields.io/docsrs/wary\u003e](https://img.shields.io/docsrs/wary)](https://docs.rs/wary/latest/wary/)\n[![ci status](https://github.com/matteopolak/wary/workflows/ci/badge.svg)](https://github.com/matteopolak/wary/actions)\n\nAn optionally `no_std` and `no_alloc` validation and transformation library.\n\n- Basic usage\n  - [Basic struct example](#basic-struct-example)\n  - [Basic enum example](#basic-enum-example)\n- [Accessing context](#context)\n- [Validation rules](#validation-rules)\n  - [Implementing custom `Rule`s](#rule-custom)\n  - [Implementing `Validate` manually](#manual-validate)\n- [Transformation rules](#transformation-rules)\n  - [Implementing custom `Transformer`s](#transformer-custom)\n  - [Implementing `Transform` manually](#manual-transform)\n\n### Basic struct example\n\n```rust\nuse std::borrow::Cow;\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name\u003c'n\u003e(\n  #[validate(alphanumeric, length(chars, 5..=20), equals(not, other = \"john\"))]\n  Cow\u003c'n, str\u003e\n);\n\n#[derive(Wary)]\nstruct Person\u003c'n\u003e {\n  #[validate(dive)]\n  name: Name\u003c'n\u003e,\n  #[validate(range(..=100))]\n  age: u8,\n}\n\nlet mut person = Person {\n  name: Name(Cow::Borrowed(\"jane\")),\n  age: 25,\n};\n\nif let Err(report) = person.wary(\u0026()) {\n  eprintln!(\"invalid person: {report:?}\");\n}\n```\n\n### Basic enum example\n\n```rust\nuse std::borrow::Cow;\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name\u003c'n\u003e(\n  #[validate(alphanumeric, length(chars, 5..=20), equals(not, other = \"john\"))]\n  #[transform(lowercase(ascii))]\n  \u0026'n mut str\n);\n\n// for length(bytes)\nimpl wary::AsRef\u003c[u8]\u003e for Name\u003c'_\u003e {\n  fn as_ref(\u0026self) -\u003e \u0026[u8] {\n    self.0.as_bytes()\n  }\n}\n\n#[derive(Wary)]\nenum Person\u003c'n\u003e {\n  Child {\n    #[validate(dive)]\n    name: Name\u003c'n\u003e,\n    #[validate(range(..=17))]\n    age: u8,\n  },\n  Adult {\n    #[validate(dive, length(bytes, ..=32))]\n    name: Name\u003c'n\u003e,\n    #[validate(range(18..=100))]\n    age: u8,\n  },\n}\n\nlet mut name = \"Jane\".to_string();\nlet mut person = Person::Adult {\n  name: Name(\u0026mut name),\n  age: 25,\n};\n\nif let Err(report) = person.wary(\u0026()) {\n  eprintln!(\"invalid person: {report:?}\");\n} else {\n  let Person::Adult { name, age } = person else {\n    unreachable!();\n  };\n\n  assert_eq!(name.0, \"jane\");\n}\n```\n\n## Accessing context \u003ca id=\"context\"\u003e\u003c/a\u003e\n\n```rust\nuse wary::Wary;\nuse wary::toolbox::rule::*;\nuse std::ops::Range;\n\n// allows one context to be passed to all rules\n#[derive(AsRef)]\nstruct Context {\n  range: Range\u003cu8\u003e,\n  #[as_ref(skip)]\n  useless: bool,\n}\n\nstruct RangeRule\u003cC\u003e {\n  ctx: PhantomData\u003cC\u003e,\n}\n\nimpl\u003cC\u003e RangeRule\u003cC\u003e {\n  fn new() -\u003e Self {\n    Self {\n      ctx: PhantomData,\n    }\n  }\n}\n\nimpl\u003cC\u003e wary::Rule\u003cu8\u003e for RangeRule\u003cC\u003e\nwhere\n  C: AsRef\u003cRange\u003cu8\u003e\u003e,\n{\n  type Context = C;\n\n  fn validate(\u0026self, ctx: \u0026Self::Context, item: \u0026u8) -\u003e Result\u003c()\u003e {\n    if ctx.as_ref().contains(item) {\n      Ok(())\n    } else {\n      Err(wary::Error::with_message(\"out_of_range\", \"The number is out of range\"))\n    }\n  }\n}\n\n#[allow(non_camel_case_types)]\nmod rule {\n  pub type range\u003cC\u003e = super::RangeRule\u003cC\u003e;\n}\n\n#[derive(Wary)]\n#[wary(context = Context)]\nstruct Age {\n  #[validate(custom(range))]\n  number: u8,\n}\n\n# fn main() {}\n```\n\n## Validation rules\n\nValidation rules applied through the proc-macro `Wary` attribute are (for the most part) simply forwarded\ndirectly to their respective builders inside the [`rule`](crate::options::rule) module. As a result of this\ndecision, all rules (except `and`, `or`, `inner`, and `dive`) will have auto-completion when writing macro attributes!\n\nIf you're providing no options to a rule, you can omit the parentheses. For example: `#[validate(alphanumeric)]`\nand `#[validate(alphanumeric())]` are equivalent.\n\n| rule | trait | feature | dependency |\n| ---- | ----- | ------- | ---------- |\n| [`addr`](#rule-addr) | [`AsRef\u003cstr\u003e`](wary::AsRef) | - | - |\n| [`alphanumeric`](#rule-alphanumeric) | [`AsRef\u003cstr\u003e`](wary::AsRef) | - | - |\n| [`and`](#rule-and) | - | - | - |\n| [`ascii`](#rule-ascii) | [`AsRef\u003cstr\u003e`](wary::AsRef) | - | - |\n| [`contains`](#rule-contains) | [`AsSlice`](wary::AsSlice) | - | - |\n| [`credit_card`](#rule-credit-card) | [`AsRef\u003cstr\u003e`](wary::AsRef) | `credit_card` | [`creditcard`](https://github.com/matteopolak/creditcard) |\n| [`custom`](#rule-custom) | [`Rule\u003cT\u003e`](wary::Rule) | - | - |\n| [`dive`](#rule-dive) | [`Validate`](wary::Validate) | - | - |\n| [`email`](#rule-email) | [`AsRef\u003cstr\u003e`](wary::AsRef) | `email` | [`email_address`](https://github.com/johnstonskj/rust-email_address) |\n| [`equals`](#rule-equals) | [`std::cmp::PartialEq`](std::cmp::PartialEq) | - | - |\n| [`func`](#rule-func) | `Fn(\u0026T) -\u003e Result\u003c(), wary::Error\u003e` | - | - |\n| [`inner`](#rule-inner) | [`AsSlice`](wary::AsSlice) | - | - |\n| [`length`](#rule-length) | [`Length`](wary::Length) | `graphemes`\\* | [`unicode-segmentation`](https://github.com/unicode-rs/unicode-segmentation) |\n| [`lowercase`](#rule-lowercase) | [`AsRef\u003cstr\u003e`](wary::AsRef) | - | - |\n| [`or`](#rule-or) | - | - | - |\n| [`prefix`](#rule-prefix) | [`AsSlice`](wary::AsSlice) | - | - |\n| [`range`](#rule-range) | [`Compare`](wary::Compare) | - | - |\n| [`regex`](#rule-regex) | [`AsRef\u003cstr\u003e`](wary::AsRef) | `regex` | [`regex`](https://github.com/rust-lang/regex) |\n| [`required`](#rule-required) | [`AsSlice`](wary::AsSlice) | - | - |\n| [`semver`](#rule-semver) | [`AsRef\u003cstr\u003e`](wary::AsRef) | `semver` | [`semver`](https://github.com/dtolnay/semver) |\n| [`suffix`](#rule-suffix) | [`AsSlice`](wary::AsSlice) | - | - |\n| [`uppercase`](#rule-uppercase) | [`AsRef\u003cstr\u003e`](wary::AsRef) | - | - |\n| [`url`](#rule-url) | [`AsRef\u003cstr\u003e`](wary::AsRef) | `url` | [`url`](https://github.com/servo/rust-url) |\n| [`uuid`](#rule-uuid) | [`AsRef\u003cstr\u003e`](wary::AsRef) | `uuid` | [`uuid`](https://github.com/uuid-rs/uuid) |\n\n\\* optional\n\n### `addr` \u003ca id=\"rule-addr\"\u003e\u003c/a\u003e\n\nValidates an address (currently only an IP).\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Packet {\n  #[validate(addr(ipv4))]\n  src: String,\n  #[validate(addr(ipv6))]\n  dest: String,\n  #[validate(addr(ip))]\n  more: String,\n}\n```\n\n### `alphanumeric` \u003ca id=\"rule-alphanumeric\"\u003e\u003c/a\u003e\n\nValidates that the input is alphanumeric.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name {\n  #[validate(alphanumeric)]\n  left: String,\n  #[validate(alphanumeric(ascii))]\n  right: String,\n}\n```\n\n### `and` \u003ca id=\"rule-and\"\u003e\u003c/a\u003e\n\nMeta-rule that combines multiple rules. Unlike other rule lists, this one **short-circuits on the first error**.\n\n```rust\nuse wary::{Wary, Validate};\n\n#[derive(Wary)]\nstruct NameAnd {\n  #[validate(and(equals(other = 1), range(2..=2)))]\n  value: u8\n}\n\nlet name = NameAnd {\n  value: 3,\n};\n\nlet report = name.validate(\u0026()).unwrap_err();\n\nassert_eq!(report.len(), 1);\n\n#[derive(Wary)]\nstruct Name {\n  #[validate(equals(other = 1), range(2..=2))]\n  value: u8\n}\n\nlet name = Name {\n  value: 3,\n};\n\nlet report = name.validate(\u0026()).unwrap_err();\n\nassert_eq!(report.len(), 2);\n```\n\n### `ascii` \u003ca id=\"rule-ascii\"\u003e\u003c/a\u003e\n\nValidates that the input is ascii.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name(\n  #[validate(ascii)]\n  String\n);\n```\n\n### `contains` \u003ca id=\"rule-contains\"\u003e\u003c/a\u003e\n\nValidates that the input contains a substring or subslice.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name(\n  #[validate(contains(str = \"hello\"))]\n  String\n);\n```\n\n### `credit_card` (requires feature `credit_card`) \u003ca id=\"rule-credit-card\"\u003e\u003c/a\u003e\n\nValidates that the input is a credit card number (PAN).\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Card(\n  #[validate(credit_card)]\n  String\n);\n```\n\n### `custom` \u003ca id=\"rule-custom\"\u003e\u003c/a\u003e\n\nValidates the input with a custom [`Rule`](wary::Rule).\n\n```rust\nuse wary::Wary;\nuse wary::toolbox::rule::*;\n\nstruct SecretRule;\n\nimpl SecretRule {\n  fn new() -\u003e Self {\n    Self\n  }\n}\n\nimpl\u003cI\u003e wary::Rule\u003cI\u003e for SecretRule\nwhere\n  I: AsRef\u003cstr\u003e,\n{\n  type Context = ();\n\n  fn validate(\u0026self, _ctx: \u0026Self::Context, item: \u0026I) -\u003e Result\u003c()\u003e {\n    let string = item.as_ref();\n\n    if string.contains(\"secret\") {\n      Err(Error::with_message(\"secret_found\", \"You cannot use the word 'secret'\"))\n    } else {\n      Ok(())\n    }\n  }\n}\n\n#[allow(non_camel_case_types)]\nmod rule {\n  pub type secret = super::SecretRule;\n}\n\n#[derive(Wary)]\nstruct Person {\n  #[validate(custom(secret))]\n  name: String,\n}\n\n# fn main() {}\n```\n\n### `dive` \u003ca id=\"rule-dive\"\u003e\u003c/a\u003e\n\nValidates the inner fields of a struct or enum.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Item {\n  #[validate(ascii)]\n  name: \u0026'static str,\n}\n\n#[derive(Wary)]\nstruct Name {\n  #[validate(dive)]\n  item: Item,\n}\n```\n\n### `email` (requires feature `email`) \u003ca id=\"rule-email\"\u003e\u003c/a\u003e\n\nValidates that the input is an email.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Email(\n  #[validate(email)]\n  String\n);\n```\n\n### `equals` \u003ca id=\"rule-equals\"\u003e\u003c/a\u003e\n\nValidates that the input is equal to a value. Currently does not support `self` fields.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name(\n  #[validate(equals(other = \"John\"))]\n  String\n);\n```\n\n### `func` \u003ca id=\"rule-func\"\u003e\u003c/a\u003e\n\nValidates the input with a function.\n\n```rust\nuse wary::{Wary, Error};\n\nfn check(_ctx: \u0026(), name: \u0026str) -\u003e Result\u003c(), Error\u003e {\n  if name.len() \u003e 5 {\n    Ok(())\n  } else {\n    Err(Error::with_message(\"name_too_short\", \"Your name must be longer than 5 characters\"))\n  }\n}\n\n#[derive(Wary)]\nstruct Name {\n  #[validate(func = |ctx: \u0026(), name: \u0026str| {\n    if name.len() \u003e 5 {\n      Ok(())\n    } else {\n      Err(Error::with_message(\"name_too_short\", \"Your name must be longer than 5 characters\"))\n    }\n  })]\n  left: String,\n  #[validate(func = check)]\n  right: String,\n}\n```\n\n### `inner` \u003ca id=\"rule-inner\"\u003e\u003c/a\u003e\n\nValidates the inner fields of a slice-like type.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name {\n  #[validate(inner(ascii))]\n  items: Vec\u003cString\u003e,\n}\n```\n\n### `length` \u003ca id=\"rule-length\"\u003e\u003c/a\u003e\n\nValidates the length of the input.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name {\n  // counts the length in bytes\n  #[validate(length(bytes, 5..=20))]\n  bytes: String,\n  // counts the length in characters\n  #[validate(length(chars, 5..=20))]\n  chars: String,\n  // counts the length in UTF-16 code units\n  #[validate(length(code_units, 5..=20))]\n  code_points: String,\n  // counts the length in grapheme clusters\n  #[validate(length(graphemes, 5..=20))]\n  graphemes: String,\n}\n```\n\n### `lowercase` \u003ca id=\"rule-lowercase\"\u003e\u003c/a\u003e\n\nValidates that the input is lowercase.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name {\n  #[validate(lowercase)]\n  left: String,\n  #[validate(lowercase(ascii))]\n  right: String,\n}\n```\n\n### `or` \u003ca id=\"rule-or\"\u003e\u003c/a\u003e\n\nMeta-rule that combines multiple rules. Short-circuits on the first success.\n\n```rust\nuse wary::{Wary, Validate};\nuse std::sync::atomic::{AtomicUsize, Ordering};\n\nmod rule {\n  pub type debug = super::DebugRule;\n}\n\nstruct DebugRule;\n\nimpl DebugRule {\n  fn new() -\u003e Self {\n    Self\n  }\n}\n\nstatic DEBUG_COUNTER: AtomicUsize = AtomicUsize::new(0);\n\nimpl\u003cI\u003e wary::Rule\u003cI\u003e for DebugRule {\n  type Context = ();\n\n  fn validate(\u0026self, _ctx: \u0026Self::Context, item: \u0026I) -\u003e Result\u003c(), wary::Error\u003e {\n    DEBUG_COUNTER.fetch_add(1, Ordering::Relaxed);\n    Ok(())\n  }\n}\n\n#[derive(Wary)]\nstruct NameOr {\n  #[validate(or(equals(other = 1), custom(debug)))]\n  value: u8\n}\n\n# fn main() {\nlet name = NameOr {\n  value: 1,\n};\n\nlet report = name.validate(\u0026()).unwrap();\n\nassert_eq!(DEBUG_COUNTER.load(Ordering::Relaxed), 0);\n# }\n```\n\n### `prefix` \u003ca id=\"rule-prefix\"\u003e\u003c/a\u003e\n\nValidates that the input starts with a substring or subslice.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name(\n  #[validate(prefix(str = \"hello\"))]\n  String\n);\n```\n\n### `range` \u003ca id=\"rule-range\"\u003e\u003c/a\u003e\n\nValidates that the input is within a range.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Age {\n  #[validate(range(18..=100))]\n  number: u8,\n  #[validate(range('a'..='z'))]\n  char: char,\n  #[validate(range(\"hello\"..\"world\"))]\n  string: String,\n}\n```\n\n### `regex` (requires feature `regex`) \u003ca id=\"rule-regex\"\u003e\u003c/a\u003e\n\nValidates that the input matches a regex.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name(\n  #[validate(regex(pat = \"^[a-z]+$\"))]\n  String\n);\n```\n\n### `required` \u003ca id=\"rule-required\"\u003e\u003c/a\u003e\n\nValidates that the input is not empty. For example, that an `Option` is `Some` or a `Vec` is not empty.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name {\n  #[validate(required)]\n  first: String,\n  #[validate(required)]\n  last: Option\u003cString\u003e,\n}\n```\n\n### `semver` (requires feature `semver`) \u003ca id=\"rule-semver\"\u003e\u003c/a\u003e\n\nValidates that the input is a semver.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Version(\n  #[validate(semver)]\n  String\n);\n```\n\n### `suffix` \u003ca id=\"rule-suffix\"\u003e\u003c/a\u003e\n\nValidates that the input ends with a substring or subslice.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name(\n  #[validate(suffix(str = \"hello\"))]\n  String\n);\n```\n\n### `uppercase` \u003ca id=\"rule-uppercase\"\u003e\u003c/a\u003e\n\nValidates that the input is uppercase.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name {\n  #[validate(uppercase)]\n  left: String,\n  #[validate(uppercase(ascii))]\n  right: String,\n}\n```\n\n### `url` (requires feature `url`) \u003ca id=\"rule-url\"\u003e\u003c/a\u003e\n\nValidates that the input is a url.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Url(\n  #[validate(url)]\n  String\n);\n```\n\n### `uuid` (requires feature `uuid`) \u003ca id=\"rule-uuid\"\u003e\u003c/a\u003e\n\nValidates that the input is a uuid.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Uuid(\n  #[validate(uuid)]\n  String\n);\n```\n\n### Implementing `Validate` manually \u003ca id=\"manual-validate\"\u003e\u003c/a\u003e\n\nIn the rare case you need to manually implement `Validate`, you will need to keep in mind about reporting errors properly.\n\n```rust\nuse wary::{Validate, Error, error::{Path, Report}};\n\nstruct Name {\n  value: String,\n}\n\nimpl Validate for Name {\n  type Context = ();\n\n  fn validate_into(\u0026self, _ctx: \u0026Self::Context, parent: \u0026Path, report: \u0026mut Report) {\n    if self.value.len() \u003c 5 {\n      report.push(\n        parent.append(\"value\"),\n        Error::with_message(\"name_too_short\", \"Your name must be longer than 5 characters\"),\n      );\n    }\n  }\n}\n\nlet name = Name {\n  value: \"Jane\".to_string(),\n};\n\nassert!(name.validate(\u0026()).is_err());\n\nlet longer = Name {\n  value: \"Jane Doe\".to_string(),\n};\n\nassert!(longer.validate(\u0026()).is_ok());\n```\n\n## Transformation rules\n\nTransformation rules are applied similarly to validation rules, but are implemented in the [`Transform`](wary::Transform) trait instead.\n\n| rule | trait | feature | dependency |\n| ---- | ----- | ------- | ---------- |\n| [`custom`](#transformer-custom) | [`Transformer`](wary::Transformer) | - | - |\n| [`dive`](#transformer-dive) | [`Transform`](wary::Transform) | - | - |\n| [`lowercase`](#transformer-lowercase) | [`AsMut\u003cstr\u003e`](wary::AsMut) (for `ascii` only) | - | - |\n| [`inner`](#transformer-inner) | [`AsMutSlice`](wary::AsMutSlice) | - | - |\n| [`uppercase`](#transformer-uppercase) | [`AsMut\u003cstr\u003e`](wary::AsMut) (for `ascii` only) | - | - |\n\n### `custom` \u003ca id=\"transformer-custom\"\u003e\u003c/a\u003e\n\nTransforms the input with a custom [`Transformer`](wary::transformer).\n\n```rust\nuse wary::{Wary, Transformer};\n\nstruct SecretTransformer;\n\nimpl SecretTransformer {\n  fn new() -\u003e Self {\n    Self\n  }\n}\n\nimpl Transformer\u003cString\u003e for SecretTransformer {\n  type Context = ();\n\n  fn transform(\u0026self, _ctx: \u0026Self::Context, item: \u0026mut String) {\n    item.clear();\n    item.push_str(\"secret\");\n  }\n}\n\n#[allow(non_camel_case_types)]\nmod transformer {\n  pub type secret = super::SecretTransformer;\n}\n\n#[derive(Wary)]\nstruct Person {\n  #[transform(custom(secret))]\n  name: String,\n}\n\n# fn main() {}\n```\n\n### `dive` \u003ca id=\"transformer-dive\"\u003e\u003c/a\u003e\n\nTransforms the inner fields of a struct or enum.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Item {\n  #[transform(lowercase)]\n  name: String,\n}\n\n#[derive(Wary)]\nstruct Name {\n  #[transform(dive)]\n  item: Item,\n}\n```\n\n### `lowercase` \u003ca id=\"transformer-lowercase\"\u003e\u003c/a\u003e\n\nTransforms the input to lowercase.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name {\n  #[transform(lowercase)]\n  left: String,\n  #[transform(lowercase(ascii))]\n  right: String,\n}\n```\n\n### `inner` \u003ca id=\"transformer-inner\"\u003e\u003c/a\u003e\n\nTransforms the inner fields of a slice-like type.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name {\n  #[transform(inner(lowercase))]\n  items: Vec\u003cString\u003e,\n}\n```\n\n### `uppercase` \u003ca id=\"transformer-uppercase\"\u003e\u003c/a\u003e\n\nTransforms the input to uppercase.\n\n```rust\nuse wary::Wary;\n\n#[derive(Wary)]\nstruct Name {\n  #[transform(uppercase)]\n  left: String,\n  #[transform(uppercase(ascii))]\n  right: String,\n}\n```\n\n### Implementing `Transform` manually \u003ca id=\"manual-transform\"\u003e\u003c/a\u003e\n\n```rust\nuse wary::Transform;\n\nstruct Name {\n  value: String,\n}\n\nimpl Transform for Name {\n  type Context = ();\n\n  fn transform(\u0026mut self, _ctx: \u0026Self::Context) {\n    self.value.make_ascii_lowercase();\n  }\n}\n\nlet mut name = Name {\n  value: \"Jane\".to_string(),\n};\n\nname.transform(\u0026());\n\nassert_eq!(name.value, \"jane\");\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatteopolak%2Fwary","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmatteopolak%2Fwary","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatteopolak%2Fwary/lists"}