readable_regex/
readable.rs

1use std::fmt::{Display, Formatter};
2use std::ops::Add;
3
4use regex::Error;
5
6use crate::{constants, solvers};
7
8#[cfg(feature = "re")]
9use regex::Regex;
10
11#[cfg(all(feature = "re-fancy", not(feature = "re")))]
12use fancy_regex::Regex;
13
14/// Enum wrapper around regex expressions, it is a recursive version of regexes
15#[derive(Clone)]
16pub enum ReadableRe<'a> {
17    /// digit match, `"\d"`
18    Digit,
19    /// word match, `"\w"`
20    Word,
21    /// whitespace match, `"\s"`
22    Whitespace,
23    /// non digit match, `"\D"`
24    NonDigit,
25    /// non word match, `"\W"`
26    NonWord,
27    /// non whitespace match, `"\S"`
28    NonWhitespace,
29    /// boundary match, `"\b"`
30    Boundary,
31
32    /// ascii letters match, `"[A-Za-z]"`
33    AsciiLetter,
34    /// ascii non letters match, `"[^A-Za-z]"`
35    AsciiNonLetter,
36    /// ascii uppercase letters match, `"[A-Z]"`
37    AsciiUppercase,
38    /// ascii non uppercase letters match, `"[^A-Z]"`
39    AsciiNonUppercase,
40    /// ascii lowercase letters match, `"[a-z]"`
41    AsciiLowercase,
42    /// ascii non lowercase letters match, `"[^a-z]"`
43    AsciiNonLowercase,
44    /// ascii alphanumerics chars match, `"[A-Za-z0-9]"`
45    AsciiAlphanumeric,
46    /// ascii non alphanumerics chars match, `"[^A-Za-z0-9]"`
47    AsciiNonAlphanumeric,
48    /// ascii numeric match, `"[0-9]"`
49    AsciiNumeric,
50    /// ascii non numeric match, `"[^0-9]"`
51    AsciiNonNumeric,
52
53    /// hexadecimal match, `"[0-9A-Fa-f]"`
54    Hexadecimal,
55    /// non hexadecimal match, `"[^0-9A-Fa-f]"`
56    NonHexadecimal,
57
58    /// anything match, `".*?"`
59    Anything,
60    /// everything match, `".*"`
61    Everything,
62    /// something match, greedy, `".+"`
63    SomethingGreedy,
64    /// something match, `".+?"`
65    Something,
66    /// any char match, `"."`
67    AnyChar,
68
69    /// escaped period, `"\."`
70    Period,
71    /// escaped caret, `"\^"`
72    Caret,
73    /// escaped dollar, `"\$"`
74    Dollar,
75    /// escaped asterisk, `"\*"`
76    Asterisk,
77    /// escaped plus sign, `"\+"`
78    PlusSign,
79    /// escaped minus sign, `"\-"`
80    MinusSign,
81    /// escaped question mark, `"\?"`
82    QuestionMark,
83    /// escaped open brace, `"\{"`
84    OpenBrace,
85    /// escaped close brace, `"\}"`
86    CloseBrace,
87    /// escaped open bracket, `"\["`
88    OpenBracket,
89    /// escaped close bracket, `"\]"`
90    CloseBracket,
91    /// escaped open parenthesis, `"\("`
92    OpenParenthesis,
93    /// escaped close bracket, `"\)"`
94    CloseParenthesis,
95    /// escaped back slash, `"\\"`
96    BackSlash,
97    /// escaped pipe, `"\|"`
98    Pipe,
99
100    /// escaped new line, `"\n"`
101    Newline,
102    /// escaped tab, `"\t"`
103    Tab,
104    /// escaped quote, `"\'"`
105    Quote,
106    /// escaped double quote, `"\""`
107    DoubleQuote,
108
109    #[cfg(feature = "re-fancy")]
110    /// back reference `"\1"`
111    ///
112    /// Available with feature `"re-fancy"`
113    Back1,
114    #[cfg(feature = "re-fancy")]
115    /// back reference `"\2"`
116    ///
117    /// Available with feature `"re-fancy"`
118    Back2,
119    #[cfg(feature = "re-fancy")]
120    /// back reference `"\3"`
121    ///
122    /// Available with feature `"re-fancy"`
123    Back3,
124    #[cfg(feature = "re-fancy")]
125    /// back reference `"\4"`
126    ///
127    /// Available with feature `"re-fancy"`
128    Back4,
129    #[cfg(feature = "re-fancy")]
130    /// back reference `"\5"`
131    /// Available with feature `"re-fancy"`
132    Back5,
133    #[cfg(feature = "re-fancy")]
134    /// back reference `"\6"`
135    ///
136    /// Available with feature `"re-fancy"`
137    Back6,
138    #[cfg(feature = "re-fancy")]
139    /// back reference `"\7"`
140    ///
141    /// Available with feature `"re-fancy"`
142    Back7,
143    #[cfg(feature = "re-fancy")]
144    /// back reference `"\8"
145    ///
146    /// Available with feature `"re-fancy"`
147    Back8,
148    #[cfg(feature = "re-fancy")]
149    /// back reference `"\9"`
150    ///
151    /// Available with feature `"re-fancy"`
152    Back9,
153
154    /// raw regex exp, `"exp"`
155    Raw(&'a str),
156    /// raw regex exp (owned), `"exp"`
157    String(String),
158    /// concatenation of regex exp, `"exp1exp2exp3"`, check [`solvers::Concat`]
159    Concat(solvers::Concat<'a>),
160
161    #[cfg(feature = "re-fancy")]
162    /// `x` backreference, `"\x"`, check [`solvers::BackReference`]
163    ///
164    /// Available with feature `"re-fancy"`
165    BackReference(solvers::BackReference),
166
167    /// Special characters escape wrapper, check [`solvers::Escape`]
168    Escape(solvers::Escape<'a>),
169    /// Regex group, `"(expr)"`, check [`solvers::Group`]
170    Group(solvers::Group<'a>),
171
172    #[cfg(feature = "re-fancy")]
173    /// look ahead, `"(?=expr)"`, check [`solvers::PositiveLookAhead`]
174    ///
175    /// Available with feature `"re-fancy"`
176    PositiveLookAhead(solvers::PositiveLookAhead<'a>),
177    #[cfg(feature = "re-fancy")]
178    /// negative look ahead, `"(?!expr)"`, check [`solvers::NegativeLookAhead`]
179    ///
180    /// Available with feature `"re-fancy"`
181    NegativeLookAhead(solvers::NegativeLookAhead<'a>),
182    #[cfg(feature = "re-fancy")]
183    /// look behind, `"(?<=expr)"`, check [`solvers::PositiveLookBehind`]
184    ///
185    /// Available with feature `"re-fancy"`
186    PositiveLookBehind(solvers::PositiveLookBehind<'a>),
187    #[cfg(feature = "re-fancy")]
188    /// negative look behind, `"(?!expr)"`, check [`solvers::NegativeLookBehind`]
189    ///
190    /// Available with feature `"re-fancy"`
191    NegativeLookBehind(solvers::NegativeLookBehind<'a>),
192    /// named group match, `"(?P<name>expr)"`, check [`solvers::NamedGroup`]
193    NamedGroup(solvers::NamedGroup<'a>),
194    /// non-capture group, `"(?:expr)"`
195    NonCaptureGroup(solvers::NonCaptureGroup<'a>),
196    /// optional match, `"expr?"`, check [`solvers::Optional`]
197    Optional(solvers::Optional<'a>),
198    /// either match, `"expr1|expr2|..."`, check [`solvers::Either`]
199    Either(solvers::Either<'a>),
200    /// exact number (n) of occurrences match, `"expr{n}"`, check [`solvers::Exactly`]
201    Exactly(solvers::Exactly<'a>),
202    /// variance number (min, max) of occurrences match, `"expr{min, max}"`, check [`solvers::Ranged`]
203    Ranged(solvers::Ranged<'a>),
204    /// zero or more occurrences match, `"expr*"`, check [`solvers::ZeroOrMore`]
205    ZeroOrMore(solvers::ZeroOrMore<'a>),
206    /// zero or more occurrences, lazy match, `"expr*?"`, , check [`solvers::ZeroOrMoreLazy`]
207    ZeroOrMoreLazy(solvers::ZeroOrMoreLazy<'a>),
208    /// one or more occurrences match, `"expr+"`, check [`solvers::OneOrMore`]
209    OneOrMore(solvers::OneOrMore<'a>),
210    /// one or more occurrences, lazy match, `"expr+?"`, , check [`solvers::OneOrMoreLazy`]
211    OneOrMoreLazy(solvers::OneOrMoreLazy<'a>),
212    /// match start, `"^expr"`, check [`solvers::StartsWith`]
213    StartsWith(solvers::StartsWith<'a>),
214    /// match end, `"expr$"`, check [`solvers::EndsWith`]
215    EndsWith(solvers::EndsWith<'a>),
216    /// match start and end, `"^expr$"`, check [`solvers::StartsAndEndsWith`]
217    StartsAndEndsWith(solvers::StartsAndEndsWith<'a>),
218    /// match characters, `"[chars]"`, check [`solvers::Chars`]
219    Chars(solvers::Chars),
220    /// exclude match characters, `"[^chars]"`, check [`solvers::NotChars`]
221    NotChars(solvers::NotChars),
222    #[cfg(feature = "re-fancy")]
223    /// match atomicly, `"(?>expr)"`, check [`solvers::AtomicGroup`]
224    /// Available with feature `"re-fancy"`
225    AtomicGroup(solvers::AtomicGroup<'a>),
226}
227
228impl Display for ReadableRe<'_> {
229    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
230        let to_write: &dyn Display = match self {
231            ReadableRe::Digit => &constants::DIGIT,
232            ReadableRe::Word => &constants::WORD,
233            ReadableRe::Whitespace => &constants::WHITESPACE,
234            ReadableRe::NonDigit => &constants::NON_DIGIT,
235            ReadableRe::NonWord => &constants::NON_WORD,
236            ReadableRe::NonWhitespace => &constants::NON_WHITESPACE,
237            ReadableRe::Boundary => &constants::BOUNDARY,
238            ReadableRe::AsciiLetter => &constants::ASCII_LETTER,
239            ReadableRe::AsciiNonLetter => &constants::ASCII_NON_LETTER,
240            ReadableRe::AsciiUppercase => &constants::ASCII_UPPERCASE,
241            ReadableRe::AsciiNonUppercase => &constants::ASCII_NON_UPPERCASE,
242            ReadableRe::AsciiLowercase => &constants::ASCII_LOWERCASE,
243            ReadableRe::AsciiNonLowercase => &constants::ASCII_NON_LOWERCASE,
244            ReadableRe::AsciiAlphanumeric => &constants::ASCII_ALPHANUMERIC,
245            ReadableRe::AsciiNonAlphanumeric => &constants::ASCII_NON_ALPHANUMERIC,
246            ReadableRe::AsciiNumeric => &constants::ASCII_NUMERIC,
247            ReadableRe::AsciiNonNumeric => &constants::ASCII_NON_NUMERIC,
248            ReadableRe::Hexadecimal => &constants::HEXADECIMAL,
249            ReadableRe::NonHexadecimal => &constants::NON_HEXADECIMAL,
250            ReadableRe::Anything => &constants::ANYTHING,
251            ReadableRe::Everything => &constants::EVERYTHING,
252            ReadableRe::SomethingGreedy => &constants::SOMETHING_GREEDY,
253            ReadableRe::Something => &constants::SOMETHING,
254            ReadableRe::AnyChar => &constants::ANY_CHAR,
255            ReadableRe::Period => &constants::PERIOD,
256            ReadableRe::Caret => &constants::CARET,
257            ReadableRe::Dollar => &constants::DOLLAR_SIGN,
258            ReadableRe::Asterisk => &constants::ASTERISK,
259            ReadableRe::PlusSign => &constants::PLUS_SIGN,
260            ReadableRe::MinusSign => &constants::MINUS_SIGN,
261            ReadableRe::QuestionMark => &constants::QUESTION_MARK,
262            ReadableRe::OpenBrace => &constants::OPEN_BRACE,
263            ReadableRe::CloseBrace => &constants::CLOSE_BRACE,
264            ReadableRe::OpenBracket => &constants::OPEN_BRACKET,
265            ReadableRe::CloseBracket => &constants::CLOSE_BRACKET,
266            ReadableRe::OpenParenthesis => &constants::OPEN_PARENTHESIS,
267            ReadableRe::CloseParenthesis => &constants::CLOSE_PARENTHESIS,
268            ReadableRe::BackSlash => &constants::BACKSLASH,
269            ReadableRe::Pipe => &constants::PIPE,
270            ReadableRe::Newline => &constants::NEWLINE,
271            ReadableRe::Tab => &constants::TAB,
272            ReadableRe::Quote => &constants::QUOTE,
273            ReadableRe::DoubleQuote => &constants::DOUBLE_QUOTE,
274            #[cfg(feature = "re-fancy")]
275            ReadableRe::Back1 => &constants::BACK_1,
276            #[cfg(feature = "re-fancy")]
277            ReadableRe::Back2 => &constants::BACK_2,
278            #[cfg(feature = "re-fancy")]
279            ReadableRe::Back3 => &constants::BACK_3,
280            #[cfg(feature = "re-fancy")]
281            ReadableRe::Back4 => &constants::BACK_4,
282            #[cfg(feature = "re-fancy")]
283            ReadableRe::Back5 => &constants::BACK_5,
284            #[cfg(feature = "re-fancy")]
285            ReadableRe::Back6 => &constants::BACK_6,
286            #[cfg(feature = "re-fancy")]
287            ReadableRe::Back7 => &constants::BACK_7,
288            #[cfg(feature = "re-fancy")]
289            ReadableRe::Back8 => &constants::BACK_8,
290            #[cfg(feature = "re-fancy")]
291            ReadableRe::Back9 => &constants::BACK_9,
292            ReadableRe::Raw(raw) => raw,
293            ReadableRe::String(s) => s,
294            ReadableRe::Concat(concat) => concat,
295            #[cfg(feature = "re-fancy")]
296            ReadableRe::BackReference(back_reference) => back_reference,
297            ReadableRe::Escape(scape) => scape,
298            ReadableRe::Group(group) => group,
299            #[cfg(feature = "re-fancy")]
300            ReadableRe::PositiveLookAhead(positive_look_ahead) => positive_look_ahead,
301            #[cfg(feature = "re-fancy")]
302            ReadableRe::NegativeLookAhead(negative_look_ahead) => negative_look_ahead,
303            #[cfg(feature = "re-fancy")]
304            ReadableRe::PositiveLookBehind(positive_look_behind) => positive_look_behind,
305            #[cfg(feature = "re-fancy")]
306            ReadableRe::NegativeLookBehind(negative_look_behind) => negative_look_behind,
307            ReadableRe::NamedGroup(named_group) => named_group,
308            ReadableRe::NonCaptureGroup(non_capture_group) => non_capture_group,
309            ReadableRe::Optional(optional) => optional,
310            ReadableRe::Either(either) => either,
311            ReadableRe::Exactly(exactly) => exactly,
312            ReadableRe::Ranged(between) => between,
313            ReadableRe::ZeroOrMore(zero_or_more) => zero_or_more,
314            ReadableRe::ZeroOrMoreLazy(zero_or_more_lazy) => zero_or_more_lazy,
315            ReadableRe::OneOrMore(one_or_more) => one_or_more,
316            ReadableRe::OneOrMoreLazy(one_or_more_lazy) => one_or_more_lazy,
317            ReadableRe::StartsWith(starts_with) => starts_with,
318            ReadableRe::EndsWith(ends_with) => ends_with,
319            ReadableRe::StartsAndEndsWith(starts_and_ends_with) => starts_and_ends_with,
320            ReadableRe::Chars(chars) => chars,
321            ReadableRe::NotChars(not_chars) => not_chars,
322            #[cfg(feature = "re-fancy")]
323            ReadableRe::AtomicGroup(atomic_group) => atomic_group,
324        };
325        write!(f, "{}", to_write)
326    }
327}
328
329impl<'a> ReadableRe<'a> {
330    pub fn compile(&self) -> Result<Regex, Error> {
331        Regex::new(&format!("{self}"))
332    }
333}
334
335impl<'a> Add<Self> for ReadableRe<'a> {
336    type Output = Self;
337
338    fn add(self, rhs: Self) -> Self::Output {
339        match (self, rhs) {
340            (Self::Concat(solvers::Concat(mut v1)), Self::Concat(solvers::Concat(v2))) => {
341                v1.extend(v2);
342                Self::Concat(solvers::Concat(v1))
343            }
344            (Self::Concat(solvers::Concat(mut v1)), other) => {
345                v1.push(other);
346                Self::Concat(solvers::Concat(v1))
347            }
348            (other, Self::Concat(solvers::Concat(v1))) => {
349                Self::Concat(solvers::Concat::new(std::iter::once(other).chain(v1)))
350            }
351            (re1, re2) => Self::Concat(solvers::Concat::new([re1, re2])),
352        }
353    }
354}
355
356impl<'a> From<&'a str> for ReadableRe<'a> {
357    fn from(s: &'a str) -> Self {
358        Self::Raw(s)
359    }
360}
361
362impl<'a> From<String> for ReadableRe<'a> {
363    fn from(s: String) -> Self {
364        Self::String(s)
365    }
366}