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
Validatederive macro. - Validations - Explains all available validations and their options.
Installation
cargo add fortifier
Features
General
macros(default) - Derive macro for theValidatetrait (fortifier-macros).message- Add a human-readablemessagefield to validation errors.
Types
all-types- Enable all features below.chrono- Support for theDateTime,NaiveDate,NaiveDateTime,NaiveTimeandTimeDeltatypes from thechronocrate.decimal- Support for theDecimaltype from therust_decimalcrate.indexmap- Support for theIndexMapandIndexSettypes from theindexmapcrate.uuid- Support for theUuidtype from theuuidcrate.
Validations
all-validations- Enable all features below.email-address- Email address validation using theemail_addresscrate.phone-number- Phone number validation using thephonenumbercrate.regex- Regular expression validation using theregexcrate.url- URL validation using theurlcrate.
Integrations
serde- Support for theserdecrate. Derives theDeserializeandSerializetraits for validation errors.utoipa- Support for theutoipacrate. Derives theToSchematrait for validation errors.
Validate
Enum
TODO
Struct
TODO
Validations
Email Address
Note
Requires the
email-addressfeature.
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
[T](slice)[T; N](array)BTreeSetBTreeMapHashSetHashMapIndexSet(requires featureindexmap)IndexMap(requires featureindexmap)LinkedListVecVecDeque
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-numberfeature.
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
DateTime(requires featurechrono)NaiveDate(requires featurechrono)NaiveDateTime(requires featurechrono)NaiveTime(requires featurechrono)TimeDelta(requires featurechrono)Decimal(requires featuredecimal)Uuid(requires featureuuid)
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
regexfeature.
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
urlfeature.
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.