Skip to main content

deser_validate/
lib.rs

1//! Validation for [deser](https://docs.rs/deser).
2//!
3//! Values are validated while they are deserialized, so errors point at the
4//! value in the input (with its line, column and path) like the errors of
5//! the format.  Validators are types (see [`Validator`]), so they are
6//! named in adapters and in the types of fields:
7//!
8//! * The [`Check<V>`](Check) adapter (`#[deser(as = Check<V>)]`) fails the
9//!   deserialization if the value is invalid.  The type of the field does
10//!   not change.  On a type (`#[deser(deserialize_as = Check<V, _>)]`) it
11//!   checks the whole value, for rules that span fields.
12//! * [`Validated<T, V>`](Validated) keeps all errors of the value in it
13//!   instead of failing, so the value around it can still be
14//!   deserialized.  This also covers errors like a string that is given
15//!   where a number is expected.
16//! * A [`Validation`] reports all problems of an input.
17//!
18//! ```
19//! use deser::Deserialize;
20//! use deser_validate::{
21//!     Check, Email, MaxLen, NonEmpty, Validated, Validation,
22//! };
23//!
24//! #[derive(Deserialize)]
25//! struct Signup {
26//!     // must be valid, or the signup is invalid
27//!     #[deser(as = Check<(NonEmpty, MaxLen<32>)>)]
28//!     name: String,
29//!     // kept even if it is invalid
30//!     email: Validated<String, Email>,
31//!     newsletter: bool,
32//! }
33//!
34//! let input = r#"{"name": "", "email": "jane@", "newsletter": "yes"}"#;
35//! let validation = Validation::new();
36//! let rv = deser_json::Deserializer::from_str(input)
37//!     .deserialize_with::<Signup, _>(|driver| validation.setup(driver));
38//! let report = validation.finish(rv).into_result().err().unwrap();
39//! let issues: Vec<_> = report
40//!     .iter()
41//!     .map(|issue| {
42//!         format!("{}: {}", issue.path().unwrap(), issue.message())
43//!     })
44//!     .collect();
45//! assert_eq!(
46//!     issues,
47//!     [
48//!         "name: invalid value: must not be empty",
49//!         "email: invalid value: must be an email address",
50//!         "newsletter: unexpected string, expected bool",
51//!     ]
52//! );
53//! ```
54//!
55//! Types that are always valid are newtypes which check themselves:
56//!
57//! ```
58//! use deser::Deserialize;
59//! use deser_validate::Check;
60//!
61//! #[derive(Deserialize)]
62//! #[deser(transparent)]
63//! pub struct Email(#[deser(as = Check<deser_validate::Email>)] String);
64//! ```
65//!
66//! # Validators
67//!
68//! The validators of this crate are [`NonEmpty`], [`Len`] (and [`MinLen`]
69//! and [`MaxLen`]), [`Range`] (and [`Min`] and [`Max`]), [`Email`] and
70//! [`Each`], which validates the items of collections.  Tuples of
71//! validators require all of them.  The [`validator!`] macro creates
72//! validators from conditions and functions:
73//!
74//! ```
75//! use deser::Deserialize;
76//! use deser_validate::{Check, validator};
77//!
78//! // a condition and a message
79//! validator!(NonZero(port: &u16) => *port != 0, "must not be zero");
80//!
81//! // a function
82//! fn check_host(host: &str) -> Result<(), String> {
83//!     match host.contains(' ') {
84//!         true => Err(format!("`{}` is not a host name", host)),
85//!         false => Ok(()),
86//!     }
87//! }
88//! validator!(HostName(host: &str) = check_host);
89//!
90//! #[derive(Deserialize)]
91//! struct Server {
92//!     #[deser(as = Check<HostName>)]
93//!     host: String,
94//!     #[deser(as = Check<NonZero>)]
95//!     port: u16,
96//! }
97//! ```
98//!
99//! Validators of types with generics or lifetimes implement [`Validator`]
100//! themselves, the macro does not support them.
101//!
102//! Validators report a [`Violation`] with a code and parameters for
103//! programs and a message for humans.  Errors have the violation attached.
104//!
105//! # Naming Validators
106//!
107//! Validators are named for the property that valid values have, as a
108//! noun or an adjective: `Email`, `Slug`, `NonEmpty`, `NonZero`,
109//! `MaxLen<64>`.  They read as `Check<NonZero>` and do not need affixes like
110//! `Valid`, `Is` or `Rules`.  Negations start with `Non` (like
111//! [`NonZero`](std::num::NonZero)).  Validators of a whole type name the
112//! rule they check (`OrderedBounds`, not `BoundsRules`), combinators name
113//! their structure ([`Each`]).  The code of a violation is the name of the
114//! validator in snake case (`max_len`, `non_zero`), for the validators of
115//! this crate and the ones [`validator!`] creates.
116#![doc(html_logo_url = "https://raw.githubusercontent.com/mitsuhiko/deser/main/artwork/logo.svg")]
117
118mod check;
119mod macros;
120mod report;
121mod validated;
122mod validator;
123mod validators;
124
125pub use self::check::Check;
126#[doc(hidden)]
127pub use self::macros::__private;
128pub use self::macros::{IntoViolation, ValidationResult};
129pub use self::report::{Issue, Outcome, Report, Validation};
130pub use self::validated::Validated;
131pub use self::validator::{Param, Validator, Violation};
132pub use self::validators::{
133    Each, Email, Integer, Len, Length, Max, MaxLen, Min, MinLen, NonEmpty, Range,
134};
135
136// the examples of the readme are tested
137#[cfg(doctest)]
138#[doc = include_str!("../README.md")]
139struct ReadmeDoctests;