type_lib/refined.rs
1//! The [`Refined`] zero-cost wrapper around a validated value.
2
3use core::cmp::Ordering;
4use core::fmt;
5use core::hash::{Hash, Hasher};
6use core::marker::PhantomData;
7use core::ops::Deref;
8
9use crate::Validator;
10
11/// A value of type `T` that is guaranteed to satisfy the validator `V`.
12///
13/// `Refined` is the heart of the parse-dont-validate pattern: a value is checked
14/// **once**, when the wrapper is constructed, and the type then proves the
15/// invariant for the rest of the value's life. Code that receives a
16/// `Refined<T, V>` never has to re-validate, because a `Refined` that violates
17/// `V` cannot be constructed through safe code.
18///
19/// The wrapper is `#[repr(transparent)]` and stores only the value plus a
20/// zero-sized marker, so it has the exact same size and layout as `T`. The
21/// guarantee costs nothing at runtime.
22///
23/// There is deliberately no `DerefMut` and no public field: handing out a `&mut
24/// T` would let a caller mutate the value into an invalid state behind the
25/// type's back. To change a refined value, build a new one with [`Refined::new`]
26/// (or recover the inner value with [`Refined::into_inner`], change it, and
27/// re-wrap it).
28///
29/// # Examples
30///
31/// Define a rule, alias a domain type, and construct it:
32///
33/// ```rust
34/// use type_lib::{Refined, ValidationError, Validator};
35///
36/// struct NonEmpty;
37///
38/// impl<S: AsRef<str> + ?Sized> Validator<S> for NonEmpty {
39/// type Error = ValidationError;
40///
41/// fn validate(value: &S) -> Result<(), Self::Error> {
42/// if value.as_ref().is_empty() {
43/// Err(ValidationError::new("non_empty", "value must not be empty"))
44/// } else {
45/// Ok(())
46/// }
47/// }
48/// }
49///
50/// /// A username that can never be empty.
51/// type Username = Refined<String, NonEmpty>;
52///
53/// let user = Username::new("alice".to_owned());
54/// assert!(user.is_ok());
55/// assert!(Username::new(String::new()).is_err());
56/// ```
57///
58/// Read the inner value through [`Deref`], [`Refined::get`], or
59/// [`Refined::into_inner`]:
60///
61/// ```rust
62/// # use type_lib::{Refined, ValidationError, Validator};
63/// # struct NonEmpty;
64/// # impl<S: AsRef<str> + ?Sized> Validator<S> for NonEmpty {
65/// # type Error = ValidationError;
66/// # fn validate(value: &S) -> Result<(), Self::Error> {
67/// # if value.as_ref().is_empty() {
68/// # Err(ValidationError::new("non_empty", "empty"))
69/// # } else { Ok(()) }
70/// # }
71/// # }
72/// # type Username = Refined<String, NonEmpty>;
73/// # fn main() -> Result<(), ValidationError> {
74/// let user = Username::new("alice".to_owned())?;
75///
76/// assert_eq!(user.len(), 5); // via Deref to String
77/// assert_eq!(user.get(), "alice"); // borrow the inner value
78/// assert_eq!(user.into_inner(), "alice"); // take ownership back
79/// # Ok(())
80/// # }
81/// ```
82#[repr(transparent)]
83pub struct Refined<T, V: Validator<T>> {
84 value: T,
85 // `fn() -> V` keeps `Refined` covariant in `V` and unconditionally
86 // `Send + Sync + Unpin`, since `V` is only a type-level tag and is never
87 // stored or produced.
88 _validator: PhantomData<fn() -> V>,
89}
90
91impl<T, V: Validator<T>> Refined<T, V> {
92 /// Validates `value` and, on success, wraps it.
93 ///
94 /// This is the only safe way to construct a `Refined`, which is what makes
95 /// the invariant trustworthy everywhere else.
96 ///
97 /// # Errors
98 ///
99 /// Returns [`V::Error`](Validator::Error) when `value` fails `V`'s rule. The
100 /// value is dropped in that case.
101 ///
102 /// # Examples
103 ///
104 /// ```rust
105 /// # use type_lib::{Refined, ValidationError, Validator};
106 /// # struct NonEmpty;
107 /// # impl<S: AsRef<str> + ?Sized> Validator<S> for NonEmpty {
108 /// # type Error = ValidationError;
109 /// # fn validate(v: &S) -> Result<(), Self::Error> {
110 /// # if v.as_ref().is_empty() { Err(ValidationError::new("e", "empty")) } else { Ok(()) }
111 /// # }
112 /// # }
113 /// let ok = Refined::<&str, NonEmpty>::new("hi");
114 /// assert!(ok.is_ok());
115 ///
116 /// let bad = Refined::<&str, NonEmpty>::new("");
117 /// assert!(bad.is_err());
118 /// ```
119 pub fn new(value: T) -> Result<Self, V::Error> {
120 V::validate(&value)?;
121 Ok(Self {
122 value,
123 _validator: PhantomData,
124 })
125 }
126
127 /// Borrows the validated inner value.
128 ///
129 /// The same borrow is also available through [`Deref`], so methods of `T`
130 /// can be called directly on a `Refined`; `get` is the explicit form for
131 /// when inference needs a nudge.
132 ///
133 /// # Examples
134 ///
135 /// ```rust
136 /// # use type_lib::{Refined, ValidationError, Validator};
137 /// # struct AnyI32;
138 /// # impl Validator<i32> for AnyI32 {
139 /// # type Error = ValidationError;
140 /// # fn validate(_: &i32) -> Result<(), Self::Error> { Ok(()) }
141 /// # }
142 /// let n = Refined::<i32, AnyI32>::new(7).expect("always valid");
143 /// assert_eq!(*n.get(), 7);
144 /// ```
145 #[must_use]
146 pub fn get(&self) -> &T {
147 &self.value
148 }
149
150 /// Consumes the wrapper and returns the inner value.
151 ///
152 /// Use this when you need to mutate or transform the value: take it out,
153 /// change it, then re-wrap with [`Refined::new`] to re-establish the
154 /// guarantee.
155 ///
156 /// # Examples
157 ///
158 /// ```rust
159 /// # use type_lib::{Refined, ValidationError, Validator};
160 /// # struct AnyI32;
161 /// # impl Validator<i32> for AnyI32 {
162 /// # type Error = ValidationError;
163 /// # fn validate(_: &i32) -> Result<(), Self::Error> { Ok(()) }
164 /// # }
165 /// let n = Refined::<i32, AnyI32>::new(7).expect("always valid");
166 /// assert_eq!(n.into_inner(), 7);
167 /// ```
168 #[must_use]
169 pub fn into_inner(self) -> T {
170 self.value
171 }
172}
173
174impl<T, V: Validator<T>> Deref for Refined<T, V> {
175 type Target = T;
176
177 fn deref(&self) -> &Self::Target {
178 &self.value
179 }
180}
181
182impl<T, V: Validator<T>> AsRef<T> for Refined<T, V> {
183 fn as_ref(&self) -> &T {
184 &self.value
185 }
186}
187
188// The trait impls below are written by hand rather than derived. Deriving would
189// add a `V: Trait` bound, but `V` is a zero-sized marker that usually does not
190// implement `Clone`, `PartialEq`, and friends — and need not, since it is never
191// stored. Bounding only on `T` keeps `Refined` usable with any marker type.
192
193impl<T: Clone, V: Validator<T>> Clone for Refined<T, V> {
194 fn clone(&self) -> Self {
195 // The value is already valid, so cloning it cannot break the invariant;
196 // re-validation would be wasted work.
197 Self {
198 value: self.value.clone(),
199 _validator: PhantomData,
200 }
201 }
202}
203
204impl<T: Copy, V: Validator<T>> Copy for Refined<T, V> {}
205
206impl<T: fmt::Debug, V: Validator<T>> fmt::Debug for Refined<T, V> {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 f.debug_tuple("Refined").field(&self.value).finish()
209 }
210}
211
212impl<T: fmt::Display, V: Validator<T>> fmt::Display for Refined<T, V> {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 self.value.fmt(f)
215 }
216}
217
218impl<T: PartialEq, V: Validator<T>> PartialEq for Refined<T, V> {
219 fn eq(&self, other: &Self) -> bool {
220 self.value == other.value
221 }
222}
223
224impl<T: Eq, V: Validator<T>> Eq for Refined<T, V> {}
225
226impl<T: PartialOrd, V: Validator<T>> PartialOrd for Refined<T, V> {
227 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
228 self.value.partial_cmp(&other.value)
229 }
230}
231
232impl<T: Ord, V: Validator<T>> Ord for Refined<T, V> {
233 fn cmp(&self, other: &Self) -> Ordering {
234 self.value.cmp(&other.value)
235 }
236}
237
238impl<T: Hash, V: Validator<T>> Hash for Refined<T, V> {
239 fn hash<H: Hasher>(&self, state: &mut H) {
240 self.value.hash(state);
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 #![allow(clippy::unwrap_used, clippy::expect_used)]
247
248 use super::*;
249 use crate::ValidationError;
250
251 struct NonEmpty;
252
253 impl Validator<&str> for NonEmpty {
254 type Error = ValidationError;
255
256 fn validate(value: &&str) -> Result<(), Self::Error> {
257 if value.is_empty() {
258 Err(ValidationError::new("non_empty", "value must not be empty"))
259 } else {
260 Ok(())
261 }
262 }
263 }
264
265 struct Positive;
266
267 impl Validator<i32> for Positive {
268 type Error = ValidationError;
269
270 fn validate(value: &i32) -> Result<(), Self::Error> {
271 if *value > 0 {
272 Ok(())
273 } else {
274 Err(ValidationError::new("positive", "value must be > 0"))
275 }
276 }
277 }
278
279 /// A `Clone`-but-not-`Copy` value type for exercising the `Clone` impl
280 /// without pulling in `alloc`.
281 #[derive(Clone, PartialEq, Debug)]
282 struct Tag(i32);
283
284 struct AnyTag;
285
286 impl Validator<Tag> for AnyTag {
287 type Error = ValidationError;
288
289 fn validate(_value: &Tag) -> Result<(), Self::Error> {
290 Ok(())
291 }
292 }
293
294 #[test]
295 fn new_accepts_valid_and_rejects_invalid() {
296 assert!(Refined::<&str, NonEmpty>::new("ok").is_ok());
297 let err = Refined::<&str, NonEmpty>::new("").unwrap_err();
298 assert_eq!(err.code(), "non_empty");
299 }
300
301 #[test]
302 fn accessors_return_inner() {
303 let n = Refined::<i32, Positive>::new(42).unwrap();
304 assert_eq!(*n.get(), 42);
305 assert_eq!(*n.as_ref(), 42);
306 assert_eq!(*n, 42); // Deref
307 assert_eq!(n.into_inner(), 42);
308 }
309
310 #[test]
311 fn delegated_traits_compare_by_value() {
312 let a = Refined::<i32, Positive>::new(1).unwrap();
313 let b = Refined::<i32, Positive>::new(1).unwrap();
314 let c = Refined::<i32, Positive>::new(2).unwrap();
315 assert_eq!(a, b);
316 assert!(a < c);
317 }
318
319 #[test]
320 fn copy_inner_makes_refined_copy() {
321 let a = Refined::<i32, Positive>::new(3).unwrap();
322 let b = a; // Copy: `a` is still usable afterwards.
323 assert_eq!(*a.get(), 3);
324 assert_eq!(*b.get(), 3);
325 }
326
327 #[test]
328 fn clone_inner_clones_refined_without_revalidating() {
329 let a = Refined::<Tag, AnyTag>::new(Tag(7)).unwrap();
330 let b = a.clone();
331 assert_eq!(*a.get(), Tag(7));
332 assert_eq!(*b.get(), Tag(7));
333 }
334
335 #[test]
336 fn layout_is_transparent_over_t() {
337 assert_eq!(
338 core::mem::size_of::<Refined<i32, Positive>>(),
339 core::mem::size_of::<i32>(),
340 );
341 assert_eq!(
342 core::mem::size_of::<Refined<&str, NonEmpty>>(),
343 core::mem::size_of::<&str>(),
344 );
345 }
346}