refinement_types/
type_regex.rs

1//! Type-level regular expressions.
2
3pub use regex::Regex;
4
5use crate::static_str::StaticStr;
6
7#[doc(hidden)]
8pub mod import {
9    pub use std::{fmt, marker::PhantomData, sync::LazyLock};
10}
11
12/// Represents static regular expressions (as returned in [`get`] of [`TypeRegex`]).
13///
14/// [`get`]: TypeRegex::get
15pub type StaticRegex = &'static Regex;
16
17/// Represents type-level regular expressions.
18pub trait TypeRegex {
19    /// Returns the compiled regular expression.
20    fn get() -> StaticRegex;
21}
22
23/// The `invalid regex` literal.
24pub const INVALID: StaticStr = "invalid regex";
25
26/// Lifts strings to type-level regular expressions.
27///
28/// ```
29/// use refinement_types::type_regex;
30///
31/// type_regex!(Integer = "^0|[1-9][0-9]*$");
32/// ```
33///
34/// Is equivalent to:
35///
36/// ```
37/// use std::{fmt, marker::PhantomData, sync::LazyLock};
38///
39/// use refinement_types::{Regex, StaticRegex, TypeRegex};
40///
41/// struct Integer {
42///     private: PhantomData<()>,
43/// }
44///
45/// impl TypeRegex for Integer {
46///     fn get() -> StaticRegex {
47///         static REGEX: LazyLock<Regex> = LazyLock::new(|| {
48///             Regex::new("^0|[1-9][0-9]*$").expect("invalid regex")
49///         });
50///
51///         LazyLock::force(&REGEX)
52///     }
53/// }
54/// ```
55#[macro_export]
56macro_rules! type_regex {
57    ($vis: vis $name: ident = $regex: expr $(=> $doc: expr)?) => {
58        $(
59            #[doc = $doc]
60        )?
61        $vis struct $name {
62            private: $crate::type_regex::import::PhantomData<()>,
63        }
64
65        impl $crate::type_regex::TypeRegex for $name {
66            fn get() -> $crate::type_regex::StaticRegex {
67                use $crate::type_regex::import::LazyLock;
68
69                static REGEX: LazyLock<$crate::type_regex::Regex> = LazyLock::new(|| {
70                    $crate::type_regex::Regex::new($regex).expect($crate::type_regex::INVALID)
71                });
72
73                LazyLock::force(&REGEX)
74            }
75        }
76
77        impl $crate::type_regex::import::fmt::Display for $name {
78            fn fmt(
79                &self, formatter: &mut $crate::type_regex::import::fmt::Formatter<'_>
80            ) -> $crate::type_regex::import::fmt::Result {
81                use $crate::type_regex::TypeRegex;
82
83                Self::get().fmt(formatter)
84            }
85        }
86    };
87}