Skip to main content

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}