Skip to main content

validator

Macro validator 

Source
macro_rules! validator {
    (
        @impl [$(#[$meta:meta])*]
        $vis:vis $name:ident($arg:ident: &$ty:ty) $body:block ($code:expr)
    ) => { ... };
    (
        $(#[$meta:meta])*
        $vis:vis $name:ident($arg:ident: &$ty:ty) => $cond:expr, $message:expr,
        code = $code:literal $(,)?
    ) => { ... };
    (
        $(#[$meta:meta])*
        $vis:vis $name:ident($arg:ident: &$ty:ty) => $cond:expr, $message:expr $(,)?
    ) => { ... };
    (
        $(#[$meta:meta])*
        $vis:vis $name:ident($arg:ident: &$ty:ty) = $func:path, code = $code:literal $(,)?
    ) => { ... };
    (
        $(#[$meta:meta])*
        $vis:vis $name:ident($arg:ident: &$ty:ty) = $func:path $(,)?
    ) => { ... };
    (
        $(#[$meta:meta])*
        $vis:vis $name:ident($arg:ident: &$ty:ty) $body:block, code = $code:literal $(,)?
    ) => { ... };
    (
        $(#[$meta:meta])*
        $vis:vis $name:ident($arg:ident: &$ty:ty) $body:block $(,)?
    ) => { ... };
}
Expand description

Creates a validator type.

The validator is a unit struct which implements Validator for the type of its argument and for all types that borrow as it (see Borrow): a validator of str validates String, Box<str> and Cow<str> too, a validator of [T] validates Vec<T>. There are three forms:

use deser_validate::{Validator, validator};

// a condition and the message if it's false
validator!(pub NonZero(port: &u16) => *port != 0, "must not be zero");

// a function
fn check_slug(value: &str) -> Result<(), &'static str> {
    if value.bytes().all(|b| b.is_ascii_lowercase() || b == b'-') {
        Ok(())
    } else {
        Err("must be a lowercase identifier")
    }
}
validator!(pub Slug(value: &str) = check_slug);

// a block
validator!(
    /// Requires an even number of items.
    pub EvenLength(items: &[u32]) {
        if items.len() % 2 != 0 {
            return Err(format!(
                "must have an even number of items, not {}",
                items.len()
            ));
        }
        Ok(())
    }
);

assert!(NonZero::validate(&80u16).is_ok());
assert!(Slug::validate(&String::from("my-service")).is_ok());
let violation = EvenLength::validate(&vec![1, 2, 3]).unwrap_err();
assert_eq!(violation.code(), "even_length");
assert_eq!(
    violation.message(),
    "must have an even number of items, not 3"
);

Functions and blocks return a Result<(), E>, where the error is a message (&'static str, String or Cow<'static, str>) or a Violation, or a bool. Messages become violations with the name of the validator in snake case as code (NonZero has the code non_zero). Functions that return false fail with the message is not valid.

The code is part of what clients see, renaming the validator changes it. All forms accept a code that is used instead of the name as last argument, violations that functions return keep their code:

use deser_validate::{Validator, validator};

validator!(
    pub Port(port: &u16) => *port != 0,
    "must not be zero",
    code = "port"
);
assert_eq!(Port::validate(&0u16).unwrap_err().code(), "port");

The macro does not support types with generics or lifetimes (like Either<T> or Name<'a>). For those, implement Validator yourself (see there).