https://github.com/jprochazk/garde
A powerful validation library for Rust
https://github.com/jprochazk/garde
rust validation
Last synced: 19 days ago
JSON representation
A powerful validation library for Rust
- Host: GitHub
- URL: https://github.com/jprochazk/garde
- Owner: jprochazk
- License: apache-2.0
- Created: 2023-03-25T01:17:12.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2025-01-19T17:08:22.000Z (3 months ago)
- Last Synced: 2025-04-03T19:40:46.140Z (26 days ago)
- Topics: rust, validation
- Language: Rust
- Homepage:
- Size: 485 KB
- Stars: 559
- Watchers: 2
- Forks: 31
- Open Issues: 27
-
Metadata Files:
- Readme: README.md
- License: LICENSE-APACHE
Awesome Lists containing this project
- awesome-ccamel - jprochazk/garde - A powerful validation library for Rust (Rust)
README
# Garde [![Documentation]][docs.rs] [![Latest Version]][crates.io]
[docs.rs]: https://docs.rs/garde/latest/garde/
[crates.io]: https://crates.io/crates/garde
[Documentation]: https://img.shields.io/docsrs/garde
[Latest Version]: https://img.shields.io/crates/v/garde.svgA Rust validation library
- [Basic usage example](#basic-usage-example)
- [Validation rules](#available-validation-rules)
- [Length modes](#length-modes)
- [Inner type validation](#inner-type-validation)
- [Newtypes](#newtypes)
- [Handling Option](#handling-option)
- [Custom validation](#custom-validation)
- [Context/Self access](#contextself-access)
- [Implementing rules](#implementing-rules)
- [Implementing `Validate`](#implementing-validate)
- [Rule adapters](#rule-adapters)
- [Integration with web frameworks](#integration-with-web-frameworks)
- [Feature flags](#feature-flags)
- [Why `garde`?](#why-garde)### Basic usage example
To get started, install `garde`:
```text,ignore
cargo add garde -F full
```And attach the `Validate` derive to your type. `garde` will generate an implementation of the `Validate` trait for you,
allowing you to call the `validate` method.Here's what that looks like in full:
```rust
use garde::{Validate, Valid};#[derive(Validate)]
struct User<'a> {
#[garde(ascii, length(min=3, max=25))]
username: &'a str,
#[garde(length(min=15))]
password: &'a str,
}let user = User {
username: "test",
password: "not_a_very_good_password",
};if let Err(e) = user.validate() {
println!("invalid user: {e}");
}
```Garde can also validate enums:
```rust
use garde::{Validate, Valid};#[derive(Validate)]
enum Data {
Struct {
#[garde(range(min=-10, max=10))]
field: i32,
},
Tuple(
#[garde(ascii)]
String
),
}let data = Data::Struct { field: 100 };
if let Err(e) = data.validate() {
println!("invalid data: {e}");
}
```### Available validation rules
| name | format | validation | feature flag |
|--------------|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------| -------------- |
| required | `#[garde(required)]` | is value set | - |
| ascii | `#[garde(ascii)]` | only contains ASCII | - |
| alphanumeric | `#[garde(alphanumeric)]` | only letters and digits | - |
| email | `#[garde(email)]` | an email according to the HTML5 spec[^1] | `email` |
| url | `#[garde(url)]` | a URL | `url` |
| ip | `#[garde(ip)]` | an IP address (either IPv4 or IPv6) | - |
| ipv4 | `#[garde(ipv4)]` | an IPv4 address | - |
| ipv6 | `#[garde(ipv6)]` | an IPv6 address | - |
| credit card | `#[garde(credit_card)]` | a credit card number | `credit-card` |
| phone number | `#[garde(phone_number)]` | a phone number | `phone-number` |
| length | `#[garde(length(, min=, max=, equal=)]` | a container with length in `min..=max` or `equal` | - |
| matches | `#[garde(matches())]` | a field matches another field | - |
| range | `#[garde(range(min=, max=, equal=))]` | a number in the range `min..=max` or `equal` | - |
| contains | `#[garde(contains())]` | a string-like value containing a substring | - |
| prefix | `#[garde(prefix())]` | a string-like value prefixed by some string | - |
| suffix | `#[garde(suffix())]` | a string-like value suffixed by some string | - |
| pattern | `#[garde(pattern(""))]` | a string-like value matching some regular expression | `regex` |
| pattern | `#[garde(pattern())]` | a string-like value matched by some [Matcher](https://docs.rs/garde/latest/garde/rules/pattern/trait.Matcher.html) | - |
| dive | `#[garde(dive)]` | nested validation, calls `validate` on the value | - |
| skip | `#[garde(skip)]` | skip validation | - |
| custom | `#[garde(custom())]` | a custom validator | - |Additional notes:
- `required` is only available for `Option` fields.
- `dive` accepts an optional context: `#[garde(dive(self.other_field))]`
- The `` argument for `length` is [explained here](#length-modes)
- For `length` and `range`:
- If `equal` is defined, `min` and `max` must be omitted.
- Assuming `equal` is omitted, either `min` or `max` may be omitted, but not both.
- `min` and `max` use an *inclusive* upper bound (`min..=max`). Setting `min == max` is equivalent to using `equal`.
- For `contains`, `prefix`, and `suffix`, the pattern must be a string literal, because the `Pattern` API [is currently unstable](https://github.com/rust-lang/rust/issues/27721).
- Garde does not enable the default features of the `regex` crate - if you need extra regex features (e.g. Unicode) or better performance, add a dependency on `regex = "1"` to your `Cargo.toml`.If most of the fields on your struct are annotated with `#[garde(skip)]`, you may use `#[garde(allow_unvalidated)]` instead:
```rust
#[derive(garde::Validate)]
struct Foo<'a> {
#[garde(length(min = 1))]
a: &'a str,#[garde(skip)]
b: &'a str, // this field will not be validated
}#[derive(garde::Validate)]
#[garde(allow_unvalidated)]
struct Bar<'a> {
#[garde(length(min = 1))]
a: &'a str,b: &'a str, // this field will not be validated
// note the lack of `#[garde(skip)]`
}
```### Length modes
The `length` rule accepts an optional `mode` argument, which determines what _kind_ of length it will validate.
The options are:
- `simple`
- `bytes`
- `graphemes`
- `utf16`
- `chars`The `simple` is the default used when the `mode` argument is omitted. The meaning of "simple length"
depends on the type. It is currently implemented for strings, where it validates the number of bytes,
and `std::collections`, where it validates the number of items.```rust
#[derive(garde::Validate)]
struct Foo {
#[garde(length(min = 1, max = 100))]
string: String,#[garde(length(min = 1, max = 100))]
collection: Vec
}
```The `bytes`, `graphemes`, `utf16`, and `chars` exist mostly for string validation:
- `bytes` validates the number of _bytes_
- `graphemens` uses the [`unicode-segmentation`](https://docs.rs/unicode-segmentation) crate, and validates the number of _graphemes_
- `utf16` uses [`encode_utf16`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.encode_utf16), and validates the number of UTF-16 _code points_
- `chars` uses [`chars`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.chars), and validates the number of _unicode scalar values_```rust
#[derive(garde::Validate)]
struct Foo {
#[garde(length(bytes, min = 1, max = 100))]
a: String, // `a.len()`
#[garde(length(graphemes, min = 1, max = 100))]
b: String, // `b.graphemes().count()`
#[garde(length(utf16, min = 1, max = 100))]
c: String, // `c.encode_utf16().count()`
#[garde(length(chars, min = 1, max = 100))]
d: String, // `d.chars().count()`
}
```### Inner type validation
If you need to validate the "inner" type of a container, such as the `String` in `Vec`, then use the `inner` modifier:
```rust
#[derive(garde::Validate)]
struct Test {
#[garde(
length(min = 1),
inner(ascii, length(min = 1)), // wrap the rule in `inner`
)]
items: Vec,
}
```The above type would fail validation if:
- the `Vec` is empty
- any of the inner `String` elements is empty
- any of the inner `String` elements contains non-ASCII charactersTo validate a deeply-nested type, such as `Vec>`, the `inner` modifier must be nested for each level of generics:
```rust
#[derive(garde::Validate)]
struct Test {
#[garde(
length(min = 1), // applies to `Vec`
inner(inner(ascii, length(min = 1))), // applies to `String`
)]
items: Vec>,
}
```You can apply separate rules to every level of the nested type:
```rust
#[derive(garde::Validate)]
struct Test {
#[garde(
length(min = 1), // applies to `Vec`
inner(required), // applies to `Option`
inner(inner(ascii, length(min = 1))), // applies to `String`
)]
items: Vec>,
}
```### Newtypes
The best way to re-use validation rules on a field is to use the [newtype idiom](https://doc.rust-lang.org/rust-by-example/generics/new_types.html)
with `#[garde(transparent)]`:```rust
#[derive(garde::Validate)]
#[garde(transparent)]
struct Username(#[garde(length(min = 3, max = 20))] String);#[derive(garde::Validate)]
struct User {
// later used with `dive`:
#[garde(dive)]
username: Username,
}
```The `username` field in the above example will inherit all the validation rules from the `String` field on `Username`. The result is that the error path will be flattened by one level, resulting in cleaner error messages:
```rust,ignore
User {
username: Username("")
}.validate()"username: length is lower than 3"
```Without the `#[garde(transparent)]` attribute, it would instead be:
```rust,ignore
User {
username: Username("")
}.validate()"username[0]: length is lower than 3"
```Structs with the `#[garde(transparent)]` attribute may have more than one field, but there must be only one unskipped field. That means every field other than the one you wish to validate must be `#[garde(skip)]`.
### Handling Option
Every rule works on `Option` fields. The field will only be validated if it is `Some`. If you additionally want to validate that the `Option` field is `Some`, use the `required` rule:
```rust
#[derive(garde::Validate)]
struct Test {
#[garde(required, ascii, length(min = 1))]
value: Option,
}
```The above type would fail validation if:
- `value` is `None`
- the inner `value` is empty
- the inner `value` contains non-ASCII characters### Custom validation
Validation may be customized via the `custom` rule, and the `context` attribute.
The context may be any type without generic parameters. By default, the context is `()`.
```rust,ignore
#[derive(garde::Validate)]
#[garde(context(PasswordContext))]
struct User {
#[garde(custom(is_strong_password))]
password: String,
}struct PasswordContext {
min_entropy: f32,
entropy: cracken::password_entropy::EntropyEstimator,
}fn is_strong_password(value: &str, context: &PasswordContext) -> garde::Result {
let bits = context.entropy.estimate_password_entropy(value.as_bytes())
.map(|e| e.mask_entropy)
.unwrap_or(0.0);
if bits < context.min_entropy {
return Err(garde::Error::new("password is not strong enough"));
}
Ok(())
}let ctx = PasswordContext { /* ... */ };
let user = User { /* ... */ };
user.validate(&ctx)?;
```The validator function may accept the value as a reference to any type which it derefs to.
In the above example, it is possible to use `&str`, because `password` is a `String`, and `String` derefs to `&str`.The `#[garde(custom(...))]` attribute accepts any expression which evalutes to a something which implements the following trait:
```rust,ignore
FnOnce(&T, &::Context) -> garde::Result
```That means it's possible to use higher order functions:
```rust
// Returns a function which does the actual validation.
fn my_equals(other: &str) -> impl FnOnce(&str, &()) -> garde::Result + '_ {
move |value, _| {
if value != other {
return Err(garde::Error::new(format!("not equal to {other}")));
}Ok(())
}
}#[derive(garde::Validate)]
struct User {
#[garde(length(min = 1, max = 255))]
password: String,
// Combined with `self` access in rules:
#[garde(custom(my_equals(&self.password2)))]
password2: String,
}
```### Context/Self access
It's generally possible to also access the context and `self`, because they are in scope in the output of the proc macro:
```rust
struct Limits {
min: usize,
max: usize,
}struct Config {
username: Limits,
}#[derive(garde::Validate)]
#[garde(context(Config as ctx))]
struct User {
#[garde(length(min = ctx.username.min, max = ctx.username.max))]
username: String,
}
```### Implementing rules
Say you want to implement length checking for a custom string-like type.
To do this, you would implement one of the `length` traits for it, depending
on what kind of validation you are looking for.```rust
#[repr(transparent)]
pub struct MyString(String);impl garde::rules::length::HasSimpleLength for MyString {
fn length(&self) -> usize {
self.0.len()
}
}
#[derive(garde::Validate)]
struct Foo {
// Now the `length` check may be used with `MyString`
#[garde(length(min = 1, max = 1000))]
field: MyString,
}
```Each rule comes with its own trait that may be implemented by custom types in your code.
They are all available under `garde::rules`.### Implementing `Validate`
In case you have a container type for which you'd like to support nested validation (using the `#[garde(dive)]` rule),
you may implement `Validate` for it:```rust
#[repr(transparent)]
struct MyVec(Vec);impl garde::Validate for MyVec {
type Context = T::Context;fn validate_into(
&self,
ctx: &Self::Context,
mut parent: &mut dyn FnMut() -> garde::Path,
report: &mut garde::Report
) {
for (index, item) in self.0.iter().enumerate() {
let mut path = garde::util::nested_path!(parent, index);
item.validate_into(ctx, &mut path, report);
}
}
}#[derive(garde::Validate)]
struct Foo {
#[garde(dive)]
field: MyVec,
}#[derive(garde::Validate)]
struct Bar {
#[garde(range(min = 1, max = 10))]
value: u32,
}
```### Rule adapters
Adapters allow you to implement validation for third-party types without using a newtype.
An adapter may look like this:
```rust
mod my_str_adapter {
#![allow(unused_imports)]
pub use garde::rules::*; // re-export garde's rulespub mod length {
pub use garde::rules::length::*; // re-export `length` rulespub mod simple {
// re-implement `simple`, but _only_ for the concrete type &str!
pub fn apply(v: &str, (min, max): (usize, usize)) -> garde::Result {
if !(min..=max).contains(&v.len()) {
Err(garde::Error::new("my custom error message"))
} else {
Ok(())
}
}
}
}
}
```You create a module, add a public glob re-export of `garde::rules` inside of it,
and then re-implement the specific rule you're interested in. This is a form of
[duck typing](https://en.wikipedia.org/wiki/Duck_typing). Any rule which you have
not re-implemented is simply delegated to `garde`'s impl.It's quite verbose, but in exchange it is maximally flexible. To use the adapter,
add an `adapt` attribute to a field:
```rust,ignore
#[derive(garde::Validate)]
struct Stuff<'a> {
#[garde(
adapt(my_str_adapter),
length(min = 1),
ascii,
)]
v: &'a str,
}
```The `length` rule will now use your custom implementation, but the `ascii` rule
will continue to use `garde`'s implementation.### Integration with web frameworks
- [`axum`](https://crates.io/crates/axum): [`axum-valid`](https://crates.io/crates/axum-valid)
- [`actix-web`](https://crates.io/crates/actix-web): [`garde-actix-web`](https://crates.io/crates/garde-actix-web)### Feature flags
| name | description | extra dependencies |
| -------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `derive` | Enables the usage of the `derive(Validate)` macro | [`garde_derive`](https://crates.io/crates/garde_derive) |
| `url` | Validation of URLs via the `url` crate. | [`url`](https://crates.io/crates/url) |
| `email` | Validation of emails according to [HTML5](https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address) | [`regex`](https://crates.io/crates/regex), [`once_cell`](https://crates.io/crates/once_cell) |
| `email-idna` | Support for [Internationalizing Domain Names for Applications](https://url.spec.whatwg.org/#idna) in email addresses | [`idna`](https://crates.io/crates/idna) |
| `regex` | Support for regular expressions in `pattern` via the `regex` crate | [`regex`](https://crates.io/crates/regex), [`once_cell`](https://crates.io/crates/once_cell) |
| `credit-card` | Validation of credit card numbers via the `card-validate` crate | [`card-validate`](https://crates.io/crates/card-validate) |
| `phone-number` | Validation of phone numbers via the `phonenumber` crate | [`phonenumber`](https://crates.io/crates/phonenumber) |
| `unicode` | Validation of grapheme count via the `unicode-segmentation` crate | [`unicode-segmentation`](https://docs.rs/unicode-segmentation) |### Why `garde`?
Garde means guard in French. I am not French, nor do I speak the language, but `guard` was taken, and this is close enough :).
### Development
Contributing to `garde` only requires a somewhat recent version of [`Rust`](https://www.rust-lang.org/learn/get-started).
This repository also makes use of the following tools, but they are optional:
- [`insta`](https://insta.rs/) for snapshot testing ([tests/rules](./garde_derive_tests/tests/rules/)).### License
Licensed under either of
- Apache License, Version 2.0
([LICENSE-APACHE](LICENSE-APACHE) or )
- MIT license
([LICENSE-MIT](LICENSE-MIT) or )at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.### Acknowledgements
This crate is heavily inspired by the [validator](https://github.com/Keats/validator) crate. It is essentially a full rewrite of `validator`.
The creation of this crate was prompted by [this comment](https://github.com/Keats/validator/issues/201#issuecomment-1167018511)
and a few others talking about a potential rewrite.[^1]: [HTML5 forms - valid email address](https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address)