1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! The [`Validator`] trait: reusable, type-level validation rules.
/// A reusable validation rule applied to values of type `T`.
///
/// A `Validator` is a *type-level* predicate. It carries no state and is never
/// instantiated — you implement it on a zero-sized marker type and the rule is
/// selected purely through the type system. Pairing a value with the validator
/// that vouched for it is exactly what [`Refined`](crate::Refined) does, at no
/// runtime cost.
///
/// # Choosing the value type
///
/// `T` is `?Sized`, so a rule can target an unsized type such as `str` or `[u8]`
/// and be reused for the owned forms by being generic over a borrow. The
/// `NonEmpty` rule below works for `str`, `String`, and `&str` alike because it
/// is written over `S: AsRef<str>`:
///
/// ```rust
/// use type_lib::{ValidationError, Validator};
///
/// struct NonEmpty;
///
/// impl<S: AsRef<str> + ?Sized> Validator<S> for NonEmpty {
/// type Error = ValidationError;
///
/// fn validate(value: &S) -> Result<(), Self::Error> {
/// if value.as_ref().is_empty() {
/// Err(ValidationError::new("non_empty", "value must not be empty"))
/// } else {
/// Ok(())
/// }
/// }
/// }
///
/// assert!(NonEmpty::validate("hello").is_ok());
/// assert!(NonEmpty::validate("").is_err());
/// ```
///
/// # Custom error types
///
/// [`Error`](Validator::Error) is an associated type, so a rule can report a
/// rich, structured failure instead of the bundled [`ValidationError`]:
///
/// ```rust
/// use type_lib::Validator;
///
/// #[derive(Debug, PartialEq)]
/// struct TooLong {
/// limit: usize,
/// actual: usize,
/// }
///
/// struct MaxLen8;
///
/// impl Validator<str> for MaxLen8 {
/// type Error = TooLong;
///
/// fn validate(value: &str) -> Result<(), Self::Error> {
/// let actual = value.chars().count();
/// if actual > 8 {
/// Err(TooLong { limit: 8, actual })
/// } else {
/// Ok(())
/// }
/// }
/// }
///
/// assert_eq!(
/// MaxLen8::validate("far-too-long"),
/// Err(TooLong { limit: 8, actual: 12 }),
/// );
/// ```
///
/// [`ValidationError`]: crate::ValidationError