refinement_types/
length.rs

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