Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Schema validation.

  • Synchronous & asynchronous validation
  • Enums & structs
  • Typed errors
  • Built-in validations
    • Email address
    • Length
    • Phone number
    • Range
    • Regular expression
    • URL
  • Support for Serde & Utoipa

Credits

Inspired by validator.

License

This project is available under the MIT license.

Rust for Web

The Fortifier project is part of Rust for Web.

Rust for Web creates and ports web libraries for Rust. All projects are free and open source.

Getting Started

Install

Add Fortifier to your project:

cargo add fortifier --features email-address

See Installation for more details.

Data structure

Define a data structure:

#![allow(unused)]
fn main() {
struct CreateUser {
    email_address: String,
    name: String,
}
}

Derive

Derive the Validate trait:

#![allow(unused)]
fn main() {
extern crate fortifier;
use fortifier::Validate;

#[derive(Validate)]
struct CreateUser {
    email_address: String,
    name: String,
}
}

Validations

Define validations:

#![allow(unused)]
fn main() {
extern crate fortifier;
use fortifier::Validate;

#[derive(Validate)]
struct CreateUser {
    #[validate(email_address)]
    email_address: String,

    #[validate(length(min = 1, max = 256))]
    name: String,
}
}

Validate

Fortifier supports both synchronous and asynchronous validation. This example will only use synchronous validation.

Call the validate_sync method on the data structure:

extern crate fortifier;

use fortifier::{
    EmailAddressError,
    EmailAddressErrorCode,
    LengthError,
    LengthErrorCode,
    Validate,
    ValidationErrors,
};

#[derive(Validate)]
struct CreateUser {
    #[validate(email_address)]
    email_address: String,

    #[validate(length(min = 1, max = 256))]
    name: String,
}

fn main() {
    let data = CreateUser {
        email_address: "amy.pond@example.com".to_string(),
        name: "Amy Pond".to_string(),
    };

    assert_eq!(data.validate_sync(), Ok(()));

    let data = CreateUser {
        email_address: "invalid".to_string(),
        name: "".to_string(),
    };

    assert_eq!(
        data.validate_sync(),
        Err(ValidationErrors::from_iter([
            CreateUserValidationError::EmailAddress(
                EmailAddressError::MissingSeparator {
                    code: EmailAddressErrorCode,
                },
            ),
            CreateUserValidationError::Name(
                LengthError::Min {
                    code: LengthErrorCode,
                    min: 1,
                    value: 0,
                }
            ),
        ])),
    );
}

Next Steps

  • Installation - Lists all available features.
  • Validate - Describes how to use the Validate derive macro.
  • Validations - Explains all available validations and their options.

Installation

cargo add fortifier

Features

General

  • macros (default) - Derive macro for the Validate trait (fortifier-macros).
  • message - Add a human-readable message field to validation errors.

Types

  • all-types - Enable all features below.
  • chrono - Support for the DateTime, NaiveDate, NaiveDateTime, NaiveTime and TimeDelta types from the chrono crate.
  • decimal - Support for the Decimal type from the rust_decimal crate.
  • indexmap - Support for the IndexMap and IndexSet types from the indexmap crate.
  • uuid - Support for the Uuid type from the uuid crate.

Validations

  • all-validations - Enable all features below.
  • email-address - Email address validation using the email_address crate.
  • phone-number - Phone number validation using the phonenumber crate.
  • regex - Regular expression validation using the regex crate.
  • url - URL validation using the url crate.

Integrations

  • serde - Support for the serde crate. Derives the Deserialize and Serialize traits for validation errors.
  • utoipa - Support for the utoipa crate. Derives the ToSchema trait for validation errors.

Validate

Enum

TODO

Struct

TODO

Validations

Email Address

Note

Requires the email-address feature.

Validate a string is an RFC-compliant email address using the email_address crate.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct User {
    #[validate(email_address)]
    email_address: String
}
}

Types

String

Validate the string is an RFC-compliant email address.

Email address

Validate the value is an RFC-compliant email address.

An EmailAddress can be constructed using EmailAddress::new_unchecked or with different options passed to EmailAddress::parse_with_options, so re-validation is required.

Options

allow_display_text

Whether display text is allowed. Defaults to false.

See Options::allow_display_text for details.

extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct User<'a> {
    #[validate(email_address(allow_display_text = false))]
    email_address: &'a str
}

fn main() {
    let user = User {
        email_address: "simon@example.com"
    };
    assert!(user.validate_sync().is_ok());

    let user = User {
        email_address: "Simon <simon@example.com>"
    };
    assert!(user.validate_sync().is_err());
}

allow_domain_literal

Whether domain literals are allowed. Defaults to true.

See Options::allow_domain_literal for details.

extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct User<'a> {
    #[validate(email_address(allow_domain_literal = false))]
    email_address: &'a str
}

fn main() {
    let user = User {
        email_address: "simon@localhost"
    };
    assert!(user.validate_sync().is_ok());

    let user = User {
        email_address: "simon@[127.0.0.1]"
    };
    assert!(user.validate_sync().is_err());
}

minimum_sub_domains

The minimum number of domain segments. Defaults to 0.

See Options::minimum_sub_domains for details.

extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct User<'a> {
    #[validate(email_address(minimum_sub_domains = 2))]
    email_address: &'a str
}

fn main() {
    let user = User {
        email_address: "simon@example.com"
    };
    assert!(user.validate_sync().is_ok());

    let user = User {
        email_address: "simon@localhost"
    };
    assert!(user.validate_sync().is_err());
}

Length

Validate the length of a string or iterable.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct User {
    #[validate(length(min = 1, max = 256))]
    name: String
}
}

Types

String

Validate the amount of characters (chars) in a string.

Iterable

Validate the amount of entries in an iterable.

Options

equal

The length should be equal to the specified expression.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct User {
    #[validate(length(equal = 2))]
    country_code: String
}
}

min

The length should be equal to or greater than the specified expression.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct User {
    #[validate(length(min = 1))]
    name: String
}
}

max

The length should be equal to or less than the specified expression.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct User {
    #[validate(length(max = 256))]
    name: String
}
}

Phone Number

Note

Requires the phone-number feature.

Validate a string is a specification-compliant phone number using the phonenumber crate.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct User {
    #[validate(phone_number)]
    phone_number: String
}
}

Types

String

Validate the string is a speficiation-compliant phone number.

Phone number

Validate the value is a specification-compliant phone number.

A PhoneNumber can be constructed with different options passed to phonenumber::parse, so re-validation is required.

Options

allowed_countries

A list of allowed country codes.

See phonenumber::country::Id for available country codes. This enum is re-exported as [fortifier::PhoneNumberCountry].

extern crate fortifier;

use fortifier::{PhoneNumberCountry, Validate};

#[derive(Validate)]
struct User<'a> {
    #[validate(phone_number(allowed_countries = vec![PhoneNumberCountry::GB]))]
    phone_number: &'a str
}

fn main() {
    let user = User {
        phone_number: "+44 20 7946 0000"
    };
    assert!(user.validate_sync().is_ok());

    let user = User {
        phone_number: "+31 6 12345678"
    };
    assert!(user.validate_sync().is_err());
}

default_country

Default country code to use when no country code is provided.

See phonenumber::country::Id for available country codes. This enum is re-exported as [fortifier::PhoneNumberCountry].

extern crate fortifier;

use fortifier::{PhoneNumberCountry, Validate};

#[derive(Validate)]
struct User<'a> {
    #[validate(phone_number(default_country = PhoneNumberCountry::GB))]
    phone_number: &'a str
}

fn main() {
    let user = User {
        phone_number: "020 7946 0000"
    };
    assert!(user.validate_sync().is_ok());
}

Range

Validate a value is within a range.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct Object {
    #[validate(range(exclusive_min = 0.0, max = 100.0))]
    height: f64,
}
}

Types

Boolean

Number

Character

String

Other

Options

min

The value should be equal to or greater than the specified expression.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct Object {
    #[validate(range(min = 0.0))]
    height: f64
}
}

max

The value should be equal to or less than the specified expression.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct Object {
    #[validate(range(max = 100.0))]
    height: f64
}
}

exclusive_min

The value should be greater than the specified expression.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct Object {
    #[validate(range(exclusive_min = 0.0))]
    height: f64
}
}

exclusive_max

The value should be less than the specified expression.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct Object {
    #[validate(range(exclusive_max = 100.0))]
    height: f64
}
}

Regular Expression

Note

Requires the regex feature.

Validate a string matches a regular expression using the regex crate.

#![allow(unused)]
fn main() {
extern crate fortifier;
extern crate regex;

use std::sync::LazyLock;

use fortifier::Validate;
use regex::Regex;

static COUNTRY_CODE_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"[A-Z]{2}").expect("valid regex"));

#[derive(Validate)]
struct User {
    #[validate(regex = &COUNTRY_CODE_REGEX)]
    country_code: String,
}
}

Types

String

Validate the string matches the specified regular expression.

Options

expression

The regular expression to match against.

The recommended approach for global regular expressions is to use a static LazyLock.

#![allow(unused)]
fn main() {
extern crate fortifier;
extern crate regex;

use std::sync::LazyLock;

use fortifier::Validate;
use regex::Regex;

static COUNTRY_CODE_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"[A-Z]{2}").expect("valid regex"));

#[derive(Validate)]
struct User {
    #[validate(regex(expression = &COUNTRY_CODE_REGEX))]
    country_code: String,
}
}

URL

Note

Requires the url feature.

Validate a string is a specification-compliant URL using the url crate.

#![allow(unused)]
fn main() {
extern crate fortifier;

use fortifier::Validate;

#[derive(Validate)]
struct User {
    #[validate(url)]
    url: String
}
}

Types

String

Validate the string is a specification-compliant URL.

URL

Validate the value is a specification-compliant URL.

A Url can only be constructed by parsing it, so no re-validation is performed.