refinement_types/
length.rs

1//! Predicates based on length.
2
3use core::fmt;
4
5use thiserror::Error;
6
7use crate::{
8    core::Predicate,
9    logic::{And, Not},
10};
11
12/// Represents types that have length defined for their values.
13pub trait HasLength {
14    /// Returns the value length.
15    fn length(&self) -> usize;
16}
17
18/// Represents errors that occur when the provided value has
19/// length greater than or equal to some bound.
20#[derive(Debug, Error)]
21#[error("received value with length >= {against}")]
22pub struct LtError {
23    /// The length against which the check was performed (the `N`).
24    pub against: usize,
25}
26
27impl LtError {
28    /// Constructs [`Self`].
29    pub const fn new(against: usize) -> Self {
30        Self { against }
31    }
32}
33
34/// Checks whether the given value has length less than `N`.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
36pub struct Lt<const N: usize>;
37
38impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for Lt<N> {
39    type Error = LtError;
40
41    fn check(value: &T) -> Result<(), Self::Error> {
42        if value.length() < N {
43            Ok(())
44        } else {
45            Err(Self::Error::new(N))
46        }
47    }
48
49    fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50        write!(formatter, "value with length < {N}")
51    }
52}
53
54/// Represents errors that occur when the provided value has
55/// length greater than some bound.
56#[derive(Debug, Error)]
57#[error("received value with length > {against}")]
58pub struct LeError {
59    /// The length against which the check was performed (the `N`).
60    pub against: usize,
61}
62
63impl LeError {
64    /// Constructs [`Self`].
65    pub const fn new(against: usize) -> Self {
66        Self { against }
67    }
68}
69
70/// Checks whether the given value has length less than or equal to `N`.
71#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
72pub struct Le<const N: usize>;
73
74impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for Le<N> {
75    type Error = LeError;
76
77    fn check(value: &T) -> Result<(), Self::Error> {
78        if value.length() <= N {
79            Ok(())
80        } else {
81            Err(Self::Error::new(N))
82        }
83    }
84
85    fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
86        write!(formatter, "value with length <= {N}")
87    }
88}
89
90/// Represents errors that occur when the provided value has
91/// length less than or equal to some bound.
92#[derive(Debug, Error)]
93#[error("received value with length <= {against}")]
94pub struct GtError {
95    /// The length against which the check was performed (the `N`).
96    pub against: usize,
97}
98
99impl GtError {
100    /// Constructs [`Self`].
101    pub const fn new(against: usize) -> Self {
102        Self { against }
103    }
104}
105
106/// Checks whether the given value has length greater than `N`.
107#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
108pub struct Gt<const N: usize>;
109
110impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for Gt<N> {
111    type Error = GtError;
112
113    fn check(value: &T) -> Result<(), Self::Error> {
114        if value.length() > N {
115            Ok(())
116        } else {
117            Err(Self::Error::new(N))
118        }
119    }
120
121    fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
122        write!(formatter, "value with length > {N}")
123    }
124}
125
126/// Represents errors that occur when the provided value has
127/// length less than some bound.
128#[derive(Debug, Error)]
129#[error("received value with length < {against}")]
130pub struct GeError {
131    /// The length against which the check was performed (the `N`).
132    pub against: usize,
133}
134
135impl GeError {
136    /// Constructs [`Self`].
137    pub const fn new(against: usize) -> Self {
138        Self { against }
139    }
140}
141
142/// Checks whether the given value has length greater than or equal to `N`.
143#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
144pub struct Ge<const N: usize>;
145
146impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for Ge<N> {
147    type Error = GeError;
148
149    fn check(value: &T) -> Result<(), Self::Error> {
150        if value.length() >= N {
151            Ok(())
152        } else {
153            Err(Self::Error::new(N))
154        }
155    }
156
157    fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
158        write!(formatter, "value with length >= {N}")
159    }
160}
161
162/// Represents errors that occur when the provided value has
163/// length not equal to some bound.
164#[derive(Debug, Error)]
165#[error("received value with length != {against}")]
166pub struct EqError {
167    /// The length against which the check was performed (the `N`).
168    pub against: usize,
169}
170
171impl EqError {
172    /// Constructs [`Self`].
173    pub const fn new(against: usize) -> Self {
174        Self { against }
175    }
176}
177
178/// Checks whether the given value has length equal to `N`.
179#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
180pub struct Eq<const N: usize>;
181
182impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for Eq<N> {
183    type Error = EqError;
184
185    fn check(value: &T) -> Result<(), Self::Error> {
186        if value.length() == N {
187            Ok(())
188        } else {
189            Err(Self::Error::new(N))
190        }
191    }
192
193    fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
194        write!(formatter, "value with length == {N}")
195    }
196}
197
198/// Represents errors that occur when the provided value has
199/// length equal to some bound.
200#[derive(Debug, Error)]
201#[error("received value with length == {against}")]
202pub struct NeError {
203    /// The length against which the check was performed (the `N`).
204    pub against: usize,
205}
206
207impl NeError {
208    /// Constructs [`Self`].
209    pub const fn new(against: usize) -> Self {
210        Self { against }
211    }
212}
213
214/// Checks whether the given value has length not equal to `N`.
215#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
216pub struct Ne<const N: usize>;
217
218impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for Ne<N> {
219    type Error = NeError;
220
221    #[allow(clippy::if_not_else)]
222    fn check(value: &T) -> Result<(), Self::Error> {
223        if value.length() != N {
224            Ok(())
225        } else {
226            Err(Self::Error::new(N))
227        }
228    }
229
230    fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
231        write!(formatter, "value with length != {N}")
232    }
233}
234
235/// Represents `(M, N)` intervals.
236pub type Open<const M: usize, const N: usize> = And<Gt<M>, Lt<N>>;
237
238/// Represents `[M, N)` intervals.
239pub type ClosedOpen<const M: usize, const N: usize> = And<Ge<M>, Lt<N>>;
240
241/// Represents `(M, N]` intervals.
242pub type OpenClosed<const M: usize, const N: usize> = And<Gt<M>, Le<N>>;
243
244/// Represents `[M, N]` intervals.
245pub type Closed<const M: usize, const N: usize> = And<Ge<M>, Le<N>>;
246
247/// Checks whether the given value has zero length.
248pub type Zero = Eq<0>;
249
250/// Checks whether the given value has non-zero length.
251pub type NonZero = Ne<0>;
252
253/// Represents errors when the provided value has
254/// length divided by [`divisor`] not equal to [`modulo`].
255///
256/// [`divisor`]: Self::divisor
257/// [`modulo`]: Self::modulo
258#[derive(Debug, Error)]
259#[error("received value % {divisor} != {modulo}")]
260pub struct ModError {
261    /// The divisor that the value length should be divided by (the `D`).
262    pub divisor: usize,
263    /// The expected modulo of the length division (the `M`).
264    pub modulo: usize,
265}
266
267impl ModError {
268    /// Constructs [`Self`].
269    pub const fn new(divisor: usize, modulo: usize) -> Self {
270        Self { divisor, modulo }
271    }
272}
273
274/// Checks whether the given value length divided by `D` has modulo `M`.
275#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
276pub struct Mod<const D: usize, const M: usize>;
277
278impl<const D: usize, const M: usize, T: HasLength + ?Sized> Predicate<T> for Mod<D, M> {
279    type Error = ModError;
280
281    fn check(value: &T) -> Result<(), Self::Error> {
282        if value.length() % D == M {
283            Ok(())
284        } else {
285            Err(Self::Error::new(D, M))
286        }
287    }
288
289    fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
290        write!(formatter, "length % {D} == {M}")
291    }
292}
293
294/// Checks whether the given value length is divisible by `D`.
295pub type Div<const D: usize> = Mod<D, 0>;
296
297/// Checks whether the given value length is even.
298pub type Even = Div<2>;
299
300/// Checks whether the given value length is odd.
301pub type Odd = Not<Even>;
302
303// core
304
305impl HasLength for str {
306    fn length(&self) -> usize {
307        self.len()
308    }
309}
310
311impl<T> HasLength for [T] {
312    fn length(&self) -> usize {
313        self.len()
314    }
315}
316
317impl<T: HasLength + ?Sized> HasLength for &T {
318    fn length(&self) -> usize {
319        T::length(self)
320    }
321}
322
323// prelude imports
324
325#[cfg(feature = "alloc")]
326use alloc::{boxed::Box, string::String, vec::Vec};
327
328#[cfg(any(feature = "alloc", feature = "std"))]
329impl<T: HasLength + ?Sized> HasLength for Box<T> {
330    fn length(&self) -> usize {
331        T::length(self)
332    }
333}
334
335#[cfg(any(feature = "alloc", feature = "std"))]
336impl HasLength for String {
337    fn length(&self) -> usize {
338        self.len()
339    }
340}
341
342#[cfg(any(feature = "alloc", feature = "std"))]
343impl<T> HasLength for Vec<T> {
344    fn length(&self) -> usize {
345        self.len()
346    }
347}
348
349// clone-on-write
350
351#[cfg(feature = "alloc")]
352use alloc::borrow::{Cow, ToOwned};
353
354#[cfg(all(not(feature = "alloc"), feature = "std"))]
355use std::borrow::{Cow, ToOwned};
356
357#[cfg(any(feature = "alloc", feature = "std"))]
358impl<T: ToOwned + HasLength + ?Sized> HasLength for Cow<'_, T> {
359    fn length(&self) -> usize {
360        T::length(self)
361    }
362}
363
364// pointers
365
366#[cfg(feature = "alloc")]
367use alloc::rc::Rc;
368
369#[cfg(all(not(feature = "alloc"), feature = "std"))]
370use std::rc::Rc;
371
372#[cfg(any(feature = "alloc", feature = "std"))]
373impl<T: HasLength + ?Sized> HasLength for Rc<T> {
374    fn length(&self) -> usize {
375        T::length(self)
376    }
377}
378
379#[cfg(feature = "alloc")]
380use alloc::sync::Arc;
381
382#[cfg(all(not(feature = "alloc"), feature = "std"))]
383use std::sync::Arc;
384
385#[cfg(any(feature = "alloc", feature = "std"))]
386impl<T: HasLength + ?Sized> HasLength for Arc<T> {
387    fn length(&self) -> usize {
388        T::length(self)
389    }
390}
391
392// shared collections
393
394#[cfg(feature = "alloc")]
395use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
396
397#[cfg(all(not(feature = "alloc"), feature = "std"))]
398use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
399
400#[cfg(any(feature = "alloc", feature = "std"))]
401impl<K, V> HasLength for BTreeMap<K, V> {
402    fn length(&self) -> usize {
403        self.len()
404    }
405}
406
407#[cfg(any(feature = "alloc", feature = "std"))]
408impl<T> HasLength for BTreeSet<T> {
409    fn length(&self) -> usize {
410        self.len()
411    }
412}
413
414#[cfg(any(feature = "alloc", feature = "std"))]
415impl<T> HasLength for BinaryHeap<T> {
416    fn length(&self) -> usize {
417        self.len()
418    }
419}
420
421#[cfg(any(feature = "alloc", feature = "std"))]
422impl<T> HasLength for LinkedList<T> {
423    fn length(&self) -> usize {
424        self.len()
425    }
426}
427
428#[cfg(any(feature = "alloc", feature = "std"))]
429impl<T> HasLength for VecDeque<T> {
430    fn length(&self) -> usize {
431        self.len()
432    }
433}
434
435// collections
436
437#[cfg(feature = "std")]
438use std::collections::{HashMap, HashSet};
439
440#[cfg(feature = "std")]
441impl<K, V, S> HasLength for HashMap<K, V, S> {
442    fn length(&self) -> usize {
443        self.len()
444    }
445}
446
447#[cfg(feature = "std")]
448impl<T, S> HasLength for HashSet<T, S> {
449    fn length(&self) -> usize {
450        self.len()
451    }
452}
453
454// OS strings
455
456#[cfg(feature = "std")]
457use std::ffi::{OsStr, OsString};
458
459#[cfg(feature = "std")]
460impl HasLength for OsStr {
461    fn length(&self) -> usize {
462        self.len()
463    }
464}
465
466#[cfg(feature = "std")]
467impl HasLength for OsString {
468    fn length(&self) -> usize {
469        self.len()
470    }
471}
472
473// paths (via underlying strings)
474
475#[cfg(feature = "std")]
476use std::path::{Path, PathBuf};
477
478#[cfg(feature = "std")]
479impl HasLength for Path {
480    fn length(&self) -> usize {
481        self.as_os_str().length()
482    }
483}
484
485#[cfg(feature = "std")]
486impl HasLength for PathBuf {
487    fn length(&self) -> usize {
488        self.as_os_str().length()
489    }
490}