reliakit_validate/lib.rs
1//! Composable validation traits and error types for Rust structs and values.
2//!
3//! `reliakit-validate` provides a small, focused toolkit for expressing
4//! validation rules as types. The core pieces are:
5//!
6//! - [`Validate`] — a trait that types implement to describe their validity
7//! rules.
8//! - [`Valid<T>`] — a zero-cost wrapper that carries proof of successful
9//! validation in the type system.
10//! - [`ValidationError`] — an error type that collects one or more
11//! [`Violation`]s, useful for validating multiple fields at once and
12//! returning all failures together.
13//!
14//! # Examples
15//!
16//! ## Single-field validation
17//!
18//! ```
19//! use reliakit_validate::{Validate, Valid, ValidationError};
20//!
21//! struct Username(String);
22//!
23//! impl Validate for Username {
24//! type Error = ValidationError;
25//!
26//! fn validate(&self) -> Result<(), Self::Error> {
27//! if self.0.is_empty() {
28//! return Err(ValidationError::new("username must not be empty"));
29//! }
30//! if self.0.len() > 32 {
31//! return Err(ValidationError::new("username must not exceed 32 characters"));
32//! }
33//! Ok(())
34//! }
35//! }
36//!
37//! let user = Valid::new(Username("alice".into())).unwrap();
38//! assert_eq!(user.0, "alice");
39//! ```
40//!
41//! ## Multi-field struct validation
42//!
43//! ```
44//! use reliakit_validate::{Validate, ValidationError, Violation};
45//!
46//! struct CreateUser {
47//! name: String,
48//! age: u8,
49//! }
50//!
51//! impl Validate for CreateUser {
52//! type Error = ValidationError;
53//!
54//! fn validate(&self) -> Result<(), Self::Error> {
55//! let mut errors = ValidationError::empty();
56//!
57//! if self.name.is_empty() {
58//! errors.push(Violation::with_field("name", "must not be empty"));
59//! }
60//! if self.age < 18 {
61//! errors.push(Violation::with_field("age", "must be at least 18"));
62//! }
63//!
64//! if errors.is_empty() { Ok(()) } else { Err(errors) }
65//! }
66//! }
67//!
68//! let result = CreateUser { name: String::new(), age: 15 }.validate();
69//! assert!(result.is_err());
70//! assert_eq!(result.unwrap_err().len(), 2);
71//! ```
72
73//! # Feature flags
74//!
75//! - `std` (default) enables `std::error::Error` for [`ValidationError`] and
76//! implies `alloc`.
77//! - `alloc` enables [`ValidationError`] and [`ValidateResult`], which collect
78//! multiple [`Violation`]s in a `Vec`.
79//!
80//! # `no_std`
81//!
82//! The crate supports `no_std`. The [`Validate`] trait, [`Valid<T>`], and
83//! [`Violation`] are available without `alloc`; implement [`Validate`] with your
84//! own error type in allocation-free contexts. [`ValidationError`] and
85//! [`ValidateResult`] require the `alloc` feature (enabled by default via `std`).
86
87#![cfg_attr(not(feature = "std"), no_std)]
88#![forbid(unsafe_code)]
89
90#[cfg(feature = "alloc")]
91extern crate alloc;
92
93mod error;
94mod valid;
95
96pub use error::Violation;
97#[cfg(feature = "alloc")]
98pub use error::{ValidateResult, ValidationError};
99pub use valid::Valid;
100
101/// A type that can validate itself.
102///
103/// Implement this trait to express the validity rules of a type. Use
104/// [`Valid<T>`] to wrap validated values and carry the proof in the type
105/// system.
106///
107/// # Example
108///
109/// ```
110/// use reliakit_validate::{Validate, ValidationError};
111///
112/// struct Score(u8);
113///
114/// impl Validate for Score {
115/// type Error = ValidationError;
116///
117/// fn validate(&self) -> Result<(), Self::Error> {
118/// if self.0 > 100 {
119/// return Err(ValidationError::new("score must not exceed 100"));
120/// }
121/// Ok(())
122/// }
123/// }
124///
125/// assert!(Score(100).validate().is_ok());
126/// assert!(Score(101).validate().is_err());
127/// ```
128pub trait Validate {
129 /// The error type returned when validation fails.
130 type Error;
131
132 /// Checks whether `self` satisfies its validity rules.
133 ///
134 /// Returns `Ok(())` if valid, or an error describing what failed.
135 fn validate(&self) -> Result<(), Self::Error>;
136}