#validation #newtype #sanitization

seventy

Newtype sanitization and validation

1 unstable release

new 0.1.0 Jan 15, 2025

#8 in #sanitization

Download history 73/week @ 2025-01-10

73 downloads per month

Apache-2.0

46KB
496 lines

Seventy

Rust newtype sanitization & validation

Overview

Seventy is a simple newtype sanitizer and validator.

There is no error handling. If you need to know why the newtype couldn't be created, this crate isn't for you.

Usage

use seventy::{
    builtins::{compare::*, string::*},
    seventy, Newtype,
};

#[seventy(
    sanitize(trim),
    validate(alphanumeric, length::chars(within(5..=20))),
)]
pub struct Username(String);

assert_eq!(
    Username::try_new("   username   ").unwrap().into_inner(),
    "username"
);

assert!(Username::try_new("   u$ername   ").is_err());

lib.rs:

Seventy is a simple newtype sanitizer and validator.

The [macro@seventy] procedural macro is provided to automatically implement sanitization, validation, and other logic.

Sanitizing

Sanitization mutates a target. Sanitization is run before validation.

Examples

The example below sanitizes the inner string by trimming surrounding whitespace.

use seventy::{builtins::string::*, seventy, Newtype};

#[seventy(sanitize(trim))]
pub struct Username(String);

assert_eq!(
    Username::try_new("   username   ").unwrap().into_inner(),
    "username"
);

Built-in Sanitizers

Seventy provides built-in sanitizers for common use cases.

Custom Sanitizers

Custom sanitizers can be defined with the Sanitizer trait.

The sanitizer below divides the target by the given value.

use std::ops::DivAssign;

use seventy::{core::Sanitizer, seventy, Newtype};

#[allow(non_snake_case)]
pub struct divide_by<T>(pub T);

impl<T> Sanitizer<T> for divide_by<T>
where
    T: DivAssign<T> + Copy,
{
    fn sanitize(&self, target: &mut T) {
        target.div_assign(self.0);
    }
}

#[seventy(sanitize(divide_by(5.0)))]
pub struct DivideBy(f32);

assert_eq!(DivideBy::try_new(10.0).unwrap().into_inner(), 2.0);

Validating

Validation checks if a target adheres to a set of rules. Validation is run after sanitization.

Examples

The example below validates the inner string is alphanumeric.

use seventy::{builtins::string::*, seventy, Newtype};

#[seventy(validate(alphanumeric))]
pub struct Username(String);

assert!(Username::try_new("username").is_ok());
assert!(Username::try_new("u$ername").is_err());

Built-in Validators

Seventy provides built-in validators for common use cases.

Custom Validators

Custom validators can be defined with the Validator trait.

The validator below checks if the target is even.

use seventy::{core::Validator, seventy, Newtype};

#[allow(non_snake_case)]
pub struct even_i64;

impl Validator<i64> for even_i64 {
    fn validate(&self, target: &i64) -> bool {
        target.abs() % 2 == 0
    }
}

#[seventy(validate(even_i64))]
pub struct EvenI64(i64);

assert!(EvenI64::try_new(2).is_ok());
assert!(EvenI64::try_new(3).is_err());

Errors?

Seventy does not support error handling. A validator only returns a boolean indicating whether the validation result is valid or invalid. If you need to know the specific reason why a newtype couldn't be created, Seventy is not the crate for you.

Upgrading

Upgrades automatically implement useful functionality. More about upgrades and the different types of upgrades can be found in the documentation for the [macro@seventy] procedural macro.

Examples

The example below uses the deref upgrade, which implements Deref on the newtype.

use seventy::{builtins::string::*, seventy, Newtype};

#[seventy(upgrades(deref))]
pub struct Username(String);

let username = Username::try_new("username").unwrap();
assert_eq!(*username, "username");

Incorporating it all

Examples

The example below first trims the target and then validates if the trimmed target is alphanumeric.

use seventy::{builtins::string::*, seventy, Newtype};

#[seventy(upgrades(deref), sanitize(trim), validate(alphanumeric))]
pub struct Username(String);

let username = Username::try_new("   username   ").unwrap();
assert_eq!(*username, "username");

assert!(Username::try_new("   u$ername   ").is_err());

Dependencies

~0.2–1.6MB
~30K SLoC