Muscraft / garde

Validation library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Garde   Documentation Latest Version

A Rust validation library

Basic usage example

To get started, use the Validate derive macro and add some validation rules to your type. This generates an implementation of the Validate trait for you. To use it, call the validate method on an instance of the type.

Here's what that looks like in full:

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:

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
ascii #[garde(ascii)] only contains ASCII -
alphanumeric #[garde(alphanumeric)] only letters and digits -
email #[garde(email)] an email according to the HTML5 spec1 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=<usize>, max=<usize>)] a container with length in min..=max -
byte_length #[garde(byte_length(min=<usize>, max=<usize>)] a byte sequence with length in min..=max -
range #[garde(range(min=<expr>, max=<expr>))] a number in the range min..=max -
contains #[garde(contains(<string>))] a string-like value containing a substring -
prefix #[garde(prefix(<string>))] a string-like value prefixed by some string -
suffix #[garde(suffix(<string>))] a string-like value suffixed by some string -
pattern #[garde(pattern(<regex>))] a string-like value matching some regular expression pattern
dive #[garde(dive)] nested validation, calls validate on the value -
skip #[garde(skip)] skip validation -
custom #[garde(custom(<function or closure>))] a custom validator -

Additional notes:

  • For length and range, either min or max may be omitted, but not both.
  • length and range use an inclusive upper bound (min..=max).
  • length uses .chars().count() for UTF-8 strings instead of .len().
  • For contains, prefix, and suffix, the pattern must be a string literal, because the Pattern API is currently unstable.
  • Nested validation using dive may not be combined with any other rule.

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 ().

#[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.

Implementing rules

Say you want to implement length checking for a custom string-like type. To do this, you would implement the garde::rules::length::HasLength trait for it.

#[repr(transparent)]
pub struct MyString(pub String);

impl garde::rules::length::HasLength for MyString {
    fn length(&self) -> usize {
        self.0.chars().count()
    }
}
#[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:

#[repr(transparent)]
struct MyVec<T>(Vec<T>);

impl<T: garde::Validate> garde::Validate for MyVec<T> {
    type Context = T::Context;

    fn validate(&self, ctx: &Self::Context) -> Result<(), garde::Errors> {
        garde::Errors::list(|errors| {
            for item in self.0.iter() {
                errors.push(item.validate(ctx));
            }
        })
        .finish()
    }
}

#[derive(garde::Validate)]
struct Foo {
  #[garde(dive)]
  field: MyVec<Bar>,
}

#[derive(garde::Validate)]
struct Bar {
  #[garde(range(min = 1, max = 10))]
  value: u32,
}

To make implementing the trait easier, the Errors type supports a nesting builders.

  • For list-like or tuple-like data structures, use Errors::list, and its .push method to attach nested Errors.
  • For map-like data structures, use Errors::fields, and its .insert method to attach nested Errors.
  • For a "flat" error list, use Errors::simple, and its .push method to attach individual errors.

The ListErrorBuilder::push and ListErrorBuilder::insert methods will ignore any errors which are empty (via Errors::is_empty).

Feature flags

name description extra dependencies
derive Enables the usage of the derive(Validate) macro garde_derive
url Validation of URLs via the url crate. url
email Validation of emails according to HTML5 regex, once_cell
email-idna Support for Internationalizing Domain Names for Applications in email addresses idna
pattern Validation using regular expressions via the regex crate regex, once_cell
credit-card Validation of credit card numbers via the card-validate crate card-validate
phone-number Validation of phone numbers via the phonenumber crate phonenumber
nightly-error-messages Enables usage of rustc_on_unimplemented for better error messages. This is an unstable feature and requires a nightly compiler. -

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.

This repository also makes use of the following tools, but they are optional:

  • insta for snapshot testing (tests/rules).
  • just for running recipes defined in the justfile. Run just -l to see what recipes are available.

License

Licensed under either of

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 crate. It is essentially a full rewrite of validator. The creation of this crate was prompted by this comment and a few others talking about a potential rewrite.

Footnotes

  1. HTML5 forms - valid email address

About

Validation library

License:Apache License 2.0


Languages

Language:Rust 99.6%Language:Just 0.4%