Skip to main content

refining_regex/
types.rs

1//! Type-level regular expressions.
2
3pub use regex::Regex;
4
5#[doc(hidden)]
6pub mod import {
7    pub use core::marker::PhantomData;
8
9    pub use std::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    #[must_use]
21    fn get() -> StaticRegex;
22}
23
24/// The `invalid regex` literal.
25pub const INVALID: &str = "invalid regex";
26
27/// Lifts strings to type-level regular expressions.
28///
29/// ```
30/// use refining_regex::type_regex;
31///
32/// type_regex!(pub Natural = "^0|[1-9][0-9]*$" => "Matches non-negative integers.");
33/// ```
34///
35/// Is essentially equivalent to:
36///
37/// ```
38/// use std::{marker::PhantomData, sync::LazyLock};
39///
40/// use refining_regex::types::{Regex, StaticRegex, TypeRegex, INVALID};
41///
42/// /// Matches non-negative integers.
43/// pub struct Natural {
44///     private: PhantomData<()>,
45/// }
46///
47/// impl TypeRegex for Natural {
48///     fn get() -> StaticRegex {
49///         static REGEX: LazyLock<Regex> = LazyLock::new(|| {
50///             Regex::new("^0|[1-9][0-9]*$").expect(INVALID)
51///         });
52///
53///         LazyLock::force(&REGEX)
54///     }
55/// }
56/// ```
57#[macro_export]
58macro_rules! type_regex {
59    ($vis: vis $name: ident = $regex: expr $(=> $doc: expr)?) => {
60        $(
61            #[doc = $doc]
62        )?
63        $vis struct $name {
64            private: $crate::types::import::PhantomData<()>,
65        }
66
67        impl $crate::types::TypeRegex for $name {
68            fn get() -> $crate::types::StaticRegex {
69                use $crate::types::import::LazyLock;
70
71                static REGEX: LazyLock<$crate::types::Regex> = LazyLock::new(|| {
72                    $crate::types::Regex::new($regex).expect($crate::types::INVALID)
73                });
74
75                LazyLock::force(&REGEX)
76            }
77        }
78    };
79}