A powerful validation library for Rust
A Rust validation library
Validategarde?To get started, install garde:
cargo add garde -F fullAnd 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:
use garde::{Validate, Valid};
#[derive(Validate)]
struct User {
#[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:
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}");
}| name | format | validation | feature flag |
|---|---|---|---|
| required | #[garde(required)] |
is value set | - |
| ascii | #[garde(ascii)] |
only contains ASCII | - |
| alphanumeric | #[garde(alphanumeric)] |
only letters and digits | - |
#[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 | - |
| dive | #[garde(dive)] |
nested validation, calls validate on the value |
- |
| skip | #[garde(skip)] |
skip validation | - |
| custom | #[garde(custom())] |
a custom validator | - |
| if | #[garde(if(cond = , ...))] |
conditional validation, applies rules only when condition is true | - |
Additional notes:
required is only available for Option fields.dive accepts an optional context: #[garde(dive(self.other_field))]length is explained herelength and range:equal is defined, min and max must be omitted.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.contains, prefix, and suffix, the pattern must be a string literal, because the Pattern API is currently unstable.if conditional validation:self fields and context variables (e.g., ctx).if(cond = expr, rule1, rule2, ...).cond = expr argument may appear anywhere in the rule.inner.regex cratepattern("") rules, if you need extra regex features (e.g. Unicode) or better performance, add regex = "1" to your Cargo.toml, both in [dependencies] AND [build-dependencies].If most of the fields on your struct are annotated with #[garde(skip)], you may use #[garde(allow_unvalidated)] instead:
#[derive(garde::Validate)]
struct Foo {
#[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 {
#[garde(length(min = 1))]
a: &'a str,
b: &'a str, // this field will not be validated
// note the lack of `#[garde(skip)]`
}The length rule accepts an optional mode argument, which determines what kind of length it will validate.
The options are:
simplebytesgraphemesutf16charsThe 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.
#[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 bytesgraphemens uses the unicode-segmentation crate, and validates the number of graphemesutf16 uses encode_utf16, and validates the number of UTF-16 code pointschars uses chars, and validates the number of unicode scalar values#[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()`
}If you need to validate the "inner" type of a container, such as the String in Vec, then use the inner modifier:
#[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:
Vec is emptyString elements is emptyString elements contains non-ASCII charactersTo validate a deeply-nested type, such as Vec>, the inner modifier must be nested for each level of generics:
#[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:
#[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>,
}Error messages produced by rules may be customized by implementing [I18n]:
…The default implementation is [i18n::DefaultI18n], which produces error mess
No open issues yet, or sync has not completed.