readable_regex/solvers.rs
1use crate::ReadableRe;
2use std::fmt::{Display, Formatter};
3use std::ops::{Bound, RangeBounds};
4
5macro_rules! impl_builder_from_iter {
6 ($struct_name:ident) => {
7 impl<'a> FromIterator<ReadableRe<'a>> for $struct_name<'a> {
8 fn from_iter<T: IntoIterator<Item = ReadableRe<'a>>>(iter: T) -> Self {
9 Self(Box::new(ReadableRe::Concat(Concat::new(
10 iter.into_iter().collect::<Vec<_>>(),
11 ))))
12 }
13 }
14 };
15}
16
17/// Just a [`ReadableRe`] concatenation wrapper
18///
19/// ## Example
20///
21/// ```
22/// use readable_regex::solvers::Concat;
23/// use readable_regex::ReadableRe::Raw;
24/// assert_eq!(&Concat::from_iter([Raw("foo"), Raw("bar")]).to_string(), "foobar");
25/// ```
26#[derive(Clone)]
27pub struct Concat<'a>(pub(crate) Vec<ReadableRe<'a>>);
28
29impl<'a> Concat<'a> {
30 pub fn new(v: impl IntoIterator<Item = ReadableRe<'a>>) -> Self {
31 Self::from_iter(v)
32 }
33}
34
35impl<'a> FromIterator<ReadableRe<'a>> for Concat<'a> {
36 fn from_iter<T: IntoIterator<Item = ReadableRe<'a>>>(iter: T) -> Self {
37 Self(iter.into_iter().collect())
38 }
39}
40
41impl<'a> Display for Concat<'a> {
42 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
43 for re in &self.0 {
44 write!(f, "{}", re)?;
45 }
46 Ok(())
47 }
48}
49
50#[cfg(feature = "re-fancy")]
51/// Returns a string in the regex syntax for a back reference, such as \1, \2, etc.
52/// ## Example
53/// ```
54/// use readable_regex::solvers::BackReference;
55/// let back_3 = BackReference(3);
56/// assert_eq!(back_3.to_string(), "\\3");
57/// ```
58#[derive(Clone)]
59pub struct BackReference(pub usize);
60
61#[cfg(feature = "re-fancy")]
62impl Display for BackReference {
63 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
64 write!(f, r"\{}", self.0)
65 }
66}
67
68/// A wrapper for [`regex::escape`]. Escape special characters in the input str
69/// ## Example
70/// ```
71/// use readable_regex::solvers::Escape;
72/// use readable_regex::ReadableRe;
73/// let scaped = Escape::new_str("!#$%&");
74/// assert_eq!(scaped.to_string(), "!\\#\\$%\\&");
75/// ```
76#[derive(Clone)]
77pub struct Escape<'a>(Box<ReadableRe<'a>>);
78
79impl Display for Escape<'_> {
80 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
81 write!(f, "{}", regex::escape(&self.0.to_string()))
82 }
83}
84
85impl<'a> Escape<'a> {
86 pub fn new_str(s: &'a str) -> Self {
87 Self(Box::new(ReadableRe::Raw(s)))
88 }
89
90 pub fn new(readable_regex: ReadableRe<'a>) -> Self {
91 Self(Box::new(readable_regex))
92 }
93}
94
95impl_builder_from_iter!(Escape);
96
97/// Regex syntax for a regex group surrounded by parentheses of the regex input str
98/// ### Example
99/// ```
100/// use readable_regex::solvers::{Concat, Group};
101/// use readable_regex::ReadableRe::{self, Raw};
102/// assert_eq!(Group::new(Raw("cat")).to_string(), "(cat)");
103/// assert_eq!(Group::new(ReadableRe::Concat(Concat::new([Raw("cat"), Raw("dog"), Raw("moose")]))).to_string(), "(catdogmoose)");
104/// assert_eq!(
105/// Group::new(ReadableRe::Concat(Concat::new([
106/// Raw("cat"),
107/// ReadableRe::Group(Group::new(Raw("dog"))),
108/// ReadableRe::Group(Group::new(Raw("moose"))),
109/// ]))).to_string(),
110/// "(cat(dog)(moose))",
111/// );
112/// ```
113#[derive(Clone)]
114pub struct Group<'a>(Box<ReadableRe<'a>>);
115
116impl<'a> Group<'a> {
117 pub fn new(re: ReadableRe<'a>) -> Self {
118 Self(Box::new(re))
119 }
120}
121
122impl<'a> Display for Group<'a> {
123 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
124 write!(f, "({})", self.0)
125 }
126}
127
128impl_builder_from_iter!(Group);
129
130#[cfg(feature = "re-fancy")]
131/// Regex syntax for a positive lookahead assertion of the regex strings
132/// A lookahead matches text but does not consume it in the original parsed text.
133///
134/// ## Example
135///
136/// In the following example, 'kitty' is matched but only if 'cat' follows
137/// 'kitty'. Note that the match only includes 'kitty' and not 'kittycat'.
138///
139/// ```
140/// use readable_regex::solvers::{Concat, PositiveLookAhead};
141/// use readable_regex::ReadableRe;
142/// use std::fmt::Display;
143/// let query = Concat::new([
144/// ReadableRe::Raw("kitty"),
145/// ReadableRe::PositiveLookAhead(PositiveLookAhead::new(ReadableRe::Raw("cat"))),
146/// ]);
147/// assert_eq!(
148/// query.to_string(),
149/// "kitty(?=cat)"
150/// );
151///
152/// assert!(!fancy_regex::Regex::new(&query.to_string()).unwrap().is_match("kitty").unwrap());
153/// assert!(fancy_regex::Regex::new(&query.to_string()).unwrap().is_match("kittycat").unwrap());
154/// ```
155#[derive(Clone)]
156pub struct PositiveLookAhead<'a>(Box<ReadableRe<'a>>);
157
158#[cfg(feature = "re-fancy")]
159impl<'a> PositiveLookAhead<'a> {
160 pub fn new(re: ReadableRe<'a>) -> Self {
161 Self(Box::new(re))
162 }
163}
164
165#[cfg(feature = "re-fancy")]
166impl<'a> Display for PositiveLookAhead<'a> {
167 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
168 write!(f, "(?={})", self.0)
169 }
170}
171
172#[cfg(feature = "re-fancy")]
173impl_builder_from_iter!(PositiveLookAhead);
174
175#[cfg(feature = "re-fancy")]
176/// Regex syntax for a negative lookahead assertion of the regex strings
177/// A lookahead matches text but does not consume it in the original parsed text.
178///
179/// ## Example
180///
181/// In the following example, 'kitty' is matched but only if the 'cat'
182/// does not follow 'kitty'. Note that the match only includes 'kitty' and not 'kittycat'
183///
184/// ```
185/// use readable_regex::solvers::{Concat, NegativeLookAhead};
186/// use readable_regex::ReadableRe;
187/// use std::fmt::Display;
188/// let query = Concat::new([
189/// ReadableRe::Raw("kitty"),
190/// ReadableRe::NegativeLookAhead(NegativeLookAhead::new(ReadableRe::Raw("cat"))),
191/// ]);
192/// assert_eq!(
193/// query.to_string(),
194/// "kitty(?!cat)"
195/// );
196///
197/// assert!(fancy_regex::Regex::new(&query.to_string()).unwrap().is_match("kitty").unwrap());
198/// assert!(!fancy_regex::Regex::new(&query.to_string()).unwrap().is_match("kittycat").unwrap());
199/// ```
200#[derive(Clone)]
201pub struct NegativeLookAhead<'a>(Box<ReadableRe<'a>>);
202
203#[cfg(feature = "re-fancy")]
204impl<'a> NegativeLookAhead<'a> {
205 pub fn new(re: ReadableRe<'a>) -> Self {
206 Self(Box::new(re))
207 }
208}
209
210#[cfg(feature = "re-fancy")]
211impl<'a> Display for NegativeLookAhead<'a> {
212 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
213 write!(f, "(?!{})", self.0)
214 }
215}
216
217#[cfg(feature = "re-fancy")]
218impl_builder_from_iter!(NegativeLookAhead);
219
220#[cfg(feature = "re-fancy")]
221/// Regex syntax for a positive lookbehind assertion of the input regexes.
222/// A lookbehind matches text but does not consume it in the original parsed text
223/// ## Example
224///
225/// In the following example, 'cat' is matched but only if 'kitty' is before 'cat'.
226/// Note that the match only includes 'cat' and not 'kittycat'.
227///
228/// ```
229/// use readable_regex::solvers::{Concat, PositiveLookBehind};
230/// use readable_regex::ReadableRe;
231/// use std::fmt::Display;
232/// let query = Concat::new([
233/// ReadableRe::PositiveLookBehind(PositiveLookBehind::new(ReadableRe::Raw("kitty"))),
234/// ReadableRe::Raw("cat")
235/// ]);
236/// assert_eq!(
237/// query.to_string(),
238/// "(?<=kitty)cat"
239/// );
240/// assert!(fancy_regex::Regex::new(&query.to_string()).unwrap().is_match("kittycat").unwrap());
241/// assert!(!fancy_regex::Regex::new(&query.to_string()).unwrap().is_match("cat").unwrap());
242/// ```
243#[derive(Clone)]
244pub struct PositiveLookBehind<'a>(Box<ReadableRe<'a>>);
245
246#[cfg(feature = "re-fancy")]
247impl<'a> PositiveLookBehind<'a> {
248 pub fn new(re: ReadableRe<'a>) -> Self {
249 Self(Box::new(re))
250 }
251}
252
253#[cfg(feature = "re-fancy")]
254impl<'a> Display for PositiveLookBehind<'a> {
255 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
256 write!(f, "(?<={})", self.0)
257 }
258}
259
260#[cfg(feature = "re-fancy")]
261impl_builder_from_iter!(PositiveLookBehind);
262
263#[cfg(feature = "re-fancy")]
264/// Negative lookbehind assertion of the input regex.
265/// A lookbehind matches text but does not consume it in the original parsed text
266///
267/// ## Example
268///
269/// In the following example, 'cat' is matched but only if 'kitty' is not
270/// before 'cat'. Note that the match only includes 'cat' and not 'kittycat'.
271///
272/// ```
273/// use readable_regex::solvers::{Concat, NegativeLookBehind};
274/// use readable_regex::ReadableRe;
275/// use std::fmt::Display;
276/// let query = Concat::from_iter([
277/// ReadableRe::NegativeLookBehind(NegativeLookBehind::new(ReadableRe::Raw("kitty"))),
278/// ReadableRe::Raw("cat")
279/// ]);
280/// assert_eq!(
281/// query.to_string(),
282/// "(?<!kitty)cat"
283/// );
284/// assert!(!fancy_regex::Regex::new(&query.to_string()).unwrap().is_match("kittycat").unwrap());
285/// assert!(fancy_regex::Regex::new(&query.to_string()).unwrap().is_match("black cat").unwrap());
286/// ```
287#[derive(Clone)]
288pub struct NegativeLookBehind<'a>(Box<ReadableRe<'a>>);
289
290#[cfg(feature = "re-fancy")]
291impl<'a> NegativeLookBehind<'a> {
292 pub fn new(re: ReadableRe<'a>) -> Self {
293 Self(Box::new(re))
294 }
295}
296
297#[cfg(feature = "re-fancy")]
298impl<'a> Display for NegativeLookBehind<'a> {
299 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
300 write!(f, "(?<!{})", self.0)
301 }
302}
303
304#[cfg(feature = "re-fancy")]
305impl_builder_from_iter!(NegativeLookBehind);
306
307/// Regex syntax for a named group of the input regex.
308/// Named groups can be referred to by their name rather than their group number.
309///
310/// ## Examples
311///
312/// ```
313/// use readable_regex::solvers::NamedGroup;
314/// use readable_regex::ReadableRe::Raw;
315/// use std::fmt::Display;
316/// assert_eq!(
317/// &NamedGroup::new("group_name", Raw(r"pattern_to_look_for")).to_string(),
318/// "(?P<group_name>pattern_to_look_for)"
319/// );
320/// assert_eq!(
321/// &NamedGroup::new("pobox", Raw(r"PO BOX \d{3:5}")).to_string(),
322/// "(?P<pobox>PO BOX \\d{3:5})"
323/// );
324/// ```
325#[derive(Clone)]
326pub struct NamedGroup<'a> {
327 name: &'a str,
328 regexes: Box<ReadableRe<'a>>,
329}
330
331impl<'a> NamedGroup<'a> {
332 pub fn new(name: &'a str, re: ReadableRe<'a>) -> Self {
333 Self {
334 name,
335 regexes: Box::new(re),
336 }
337 }
338}
339
340impl<'a> Display for NamedGroup<'a> {
341 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
342 write!(f, "(?P<{}>{})", self.name, self.regexes)
343 }
344}
345
346/// Regex syntax for a non-capturing group of the regex input
347///
348/// Non-capturing groups are not included in the groups field of a Pattern object.
349/// They are useful for when you want to group parts of a regex string together without
350/// affecting the numbered groups.
351///
352/// ## Example
353///
354/// ```
355/// use readable_regex::solvers::{Concat, NonCaptureGroup};
356/// use readable_regex::ReadableRe;
357/// use std::fmt::Display;
358/// let query = ReadableRe::NonCaptureGroup(
359/// NonCaptureGroup::new(ReadableRe::Raw("pattern_to_look_for"))
360/// );
361/// assert_eq!(
362/// query.to_string(),
363/// "(?:pattern_to_look_for)"
364/// );
365/// ```
366#[derive(Clone)]
367pub struct NonCaptureGroup<'a>(Box<ReadableRe<'a>>);
368
369impl<'a> NonCaptureGroup<'a> {
370 pub fn new(re: ReadableRe<'a>) -> Self {
371 Self(Box::new(re))
372 }
373}
374
375impl<'a> Display for NonCaptureGroup<'a> {
376 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
377 write!(f, "(?:{})", self.0)
378 }
379}
380
381impl_builder_from_iter!(NonCaptureGroup);
382
383/// Regex syntax for an optional part of the pattern of the regex strings in tuple_of_regex_strs
384///
385/// ## Example
386/// ```
387/// use readable_regex::solvers::Optional;
388/// use readable_regex::ReadableRe::{self, Raw};
389/// let query = ReadableRe::Optional(Optional::new(Raw("foo")));
390/// assert_eq!(
391/// query.to_string(),
392/// "foo?"
393/// );
394/// ```
395#[derive(Clone)]
396pub struct Optional<'a>(Box<ReadableRe<'a>>);
397
398impl<'a> Optional<'a> {
399 pub fn new(re: ReadableRe<'a>) -> Self {
400 Self(Box::new(re))
401 }
402}
403
404impl<'a> Display for Optional<'a> {
405 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
406 write!(f, "{}?", self.0)
407 }
408}
409
410impl_builder_from_iter!(Optional);
411
412/// Regex syntax for the alternation or "or" operator of the patterns in iterator input,
413/// and the alternation is placed in a group
414///
415/// ## Examples
416///
417/// ```
418/// use readable_regex::solvers::Either;
419/// use readable_regex::ReadableRe::Raw;
420/// assert_eq!(Either::new([Raw("a"), Raw("b"), Raw("c")]).to_string(), "a|b|c")
421/// ```
422#[derive(Clone)]
423pub struct Either<'a>(Concat<'a>);
424
425impl<'a> Either<'a> {
426 pub fn new(iter: impl IntoIterator<Item = ReadableRe<'a>>) -> Self {
427 Self::from_iter(iter)
428 }
429}
430
431impl<'a> Display for Either<'a> {
432 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
433 write!(f, "{}", self.0)
434 }
435}
436
437impl<'a> FromIterator<ReadableRe<'a>> for Either<'a> {
438 fn from_iter<T: IntoIterator<Item = ReadableRe<'a>>>(iter: T) -> Self {
439 let iter = iter
440 .into_iter()
441 .flat_map(|re| [re, ReadableRe::Raw("|")].into_iter());
442 // TODO: substitute with `interleave` when available
443 let mut concat = Concat::from_iter(iter);
444 concat.0.pop();
445 Self(concat)
446 }
447}
448
449/// Regex syntax for matching an exact number of occurrences of the input regex
450///
451/// ## Example
452///
453/// ```
454/// use readable_regex::solvers::Exactly;
455/// use readable_regex::ReadableRe::Raw;
456/// let query = Exactly::new(3, Raw("A"));
457/// assert_eq!(query.to_string(), "A{3}")
458/// ```
459#[derive(Clone)]
460pub struct Exactly<'a> {
461 quantity: usize,
462 re: Box<ReadableRe<'a>>,
463}
464
465impl<'a> Exactly<'a> {
466 pub fn new(quantity: usize, re: ReadableRe<'a>) -> Self {
467 Self {
468 quantity,
469 re: Box::new(re),
470 }
471 }
472}
473
474impl<'a> Display for Exactly<'a> {
475 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
476 write!(f, "{}{{{}}}", self.re, self.quantity)
477 }
478}
479
480/// Regex syntax for matching an between the minimum and maximum number of occurrences of the input regex
481/// It accepts unbounded ranges. It also do not discriminate between open/closed ranges, and both
482/// works as including both ends.
483///
484/// ## Example
485///
486/// ```
487/// use readable_regex::solvers::Ranged;
488/// use readable_regex::ReadableRe::Raw;
489/// let query = Ranged::new(3..5, Raw("abc"));
490/// assert_eq!(query.to_string(), "abc{3,5}");
491/// let query = Ranged::new(..5, Raw("abc"));
492/// assert_eq!(query.to_string(), "abc{,5}");
493/// let query = Ranged::new(3.., Raw("abc"));
494/// assert_eq!(query.to_string(), "abc{3,}");
495/// let query = Ranged::new(.., Raw("abc"));
496/// assert_eq!(query.to_string(), "abc{,}");
497/// ```
498pub struct Ranged<'a> {
499 range: (Bound<usize>, Bound<usize>),
500 re: Box<ReadableRe<'a>>,
501}
502
503impl<'a> Clone for Ranged<'a> {
504 fn clone(&self) -> Self {
505 Self {
506 range: self.range,
507 re: self.re.clone(),
508 }
509 }
510}
511
512impl<'a> Ranged<'a> {
513 pub fn new<R>(range: R, re: ReadableRe<'a>) -> Self
514 where
515 R: RangeBounds<usize> + 'static,
516 {
517 Self {
518 range: (range.start_bound().cloned(), range.end_bound().cloned()),
519 re: Box::new(re),
520 }
521 }
522}
523
524impl<'a> Display for Ranged<'a> {
525 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
526 let (min, max) = match self.range {
527 (
528 Bound::Included(min) | Bound::Excluded(min),
529 Bound::Included(max) | Bound::Excluded(max),
530 ) => (min.to_string(), max.to_string()),
531 (Bound::Included(min) | Bound::Excluded(min), Bound::Unbounded) => {
532 (min.to_string(), "".to_string())
533 }
534 (Bound::Unbounded, Bound::Included(max) | Bound::Excluded(max)) => {
535 ("".to_string(), max.to_string())
536 }
537 (Bound::Unbounded, Bound::Unbounded) => ("".to_string(), "".to_string()),
538 };
539 write!(f, "{}{{{},{}}}", self.re, min, max)
540 }
541}
542
543/// Regex syntax for matching zero or more occurrences of the input regex.
544/// This does a greedy match, which tries to make the largest match possible.
545///
546/// ## Example
547///
548/// ```
549/// use readable_regex::ReadableRe::Raw;
550/// use readable_regex::solvers::{ZeroOrMore};
551/// let query = ZeroOrMore::new(Raw("abc"));
552/// assert_eq!(query.to_string(), "abc*")
553/// ```
554#[derive(Clone)]
555pub struct ZeroOrMore<'a>(Box<ReadableRe<'a>>);
556
557impl<'a> ZeroOrMore<'a> {
558 pub fn new(re: ReadableRe<'a>) -> Self {
559 Self(Box::new(re))
560 }
561}
562
563impl<'a> Display for ZeroOrMore<'a> {
564 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
565 write!(f, "{}*", self.0)
566 }
567}
568
569impl_builder_from_iter!(ZeroOrMore);
570
571/// Regex syntax for matching zero or more occurrences of the input regex.
572/// This does a lazy match, which tries to make the smallest match possible.
573///
574/// ## Example
575///
576/// ```
577/// use readable_regex::ReadableRe::Raw;
578/// use readable_regex::solvers::{ZeroOrMoreLazy};
579/// let query = ZeroOrMoreLazy::new(Raw("abc"));
580/// assert_eq!(query.to_string(), "abc*?")
581/// ```
582#[derive(Clone)]
583pub struct ZeroOrMoreLazy<'a>(Box<ReadableRe<'a>>);
584
585impl<'a> ZeroOrMoreLazy<'a> {
586 pub fn new(re: ReadableRe<'a>) -> Self {
587 Self(Box::new(re))
588 }
589}
590
591impl<'a> Display for ZeroOrMoreLazy<'a> {
592 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
593 write!(f, "{}*?", self.0)
594 }
595}
596
597impl_builder_from_iter!(ZeroOrMoreLazy);
598
599/// Regex syntax for matching one or more occurrences of the input regex.
600/// This does a greedy match, which tries to make the largest match possible.
601///
602/// ## Example
603///
604/// ```
605/// use readable_regex::ReadableRe::Raw;
606/// use readable_regex::solvers::{OneOrMore};
607/// let query = OneOrMore::new(Raw("abc"));
608/// assert_eq!(query.to_string(), "abc+")
609/// ```
610#[derive(Clone)]
611pub struct OneOrMore<'a>(Box<ReadableRe<'a>>);
612
613impl<'a> OneOrMore<'a> {
614 pub fn new(re: ReadableRe<'a>) -> Self {
615 Self(Box::new(re))
616 }
617}
618
619impl<'a> Display for OneOrMore<'a> {
620 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
621 write!(f, "{}+", self.0)
622 }
623}
624
625impl_builder_from_iter!(OneOrMore);
626
627/// Regex syntax for matching one or more occurrences of the input regex.
628/// This does a lazy match, which tries to make the smallest match possible.
629///
630/// ## Example
631///
632/// ```
633/// use readable_regex::ReadableRe::Raw;
634/// use readable_regex::solvers::{OneOrMoreLazy};
635/// let query = OneOrMoreLazy::new(Raw("abc"));
636/// assert_eq!(query.to_string(), "abc+?")
637/// ```
638#[derive(Clone)]
639pub struct OneOrMoreLazy<'a>(Box<ReadableRe<'a>>);
640
641impl<'a> OneOrMoreLazy<'a> {
642 pub fn new(re: ReadableRe<'a>) -> Self {
643 Self(Box::new(re))
644 }
645}
646
647impl<'a> Display for OneOrMoreLazy<'a> {
648 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
649 write!(f, "{}+?", self.0)
650 }
651}
652
653impl_builder_from_iter!(OneOrMoreLazy);
654
655/// Regex syntax for matching the pattern of the input regex at the start of the searched text.
656///
657/// ## Example
658///
659/// ```
660/// use readable_regex::solvers::StartsWith;
661/// use readable_regex::ReadableRe::Raw;
662/// let query = StartsWith::new(Raw("abc"));
663/// assert_eq!(query.to_string(), "^abc");
664/// ```
665#[derive(Clone)]
666pub struct StartsWith<'a>(Box<ReadableRe<'a>>);
667
668impl<'a> StartsWith<'a> {
669 pub fn new(re: ReadableRe<'a>) -> Self {
670 Self(Box::new(re))
671 }
672}
673
674impl<'a> Display for StartsWith<'a> {
675 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
676 write!(f, "^{}", self.0)
677 }
678}
679
680impl_builder_from_iter!(StartsWith);
681
682/// Regex syntax for matching the pattern of the input regex at the end of the searched text.
683///
684/// ## Example
685///
686/// ```
687/// use readable_regex::solvers::EndsWith;
688/// use readable_regex::ReadableRe::Raw;
689/// let query = EndsWith::new(Raw("abc"));
690/// assert_eq!(query.to_string(), "abc$");
691/// ```
692#[derive(Clone)]
693pub struct EndsWith<'a>(Box<ReadableRe<'a>>);
694
695impl<'a> EndsWith<'a> {
696 pub fn new(re: ReadableRe<'a>) -> Self {
697 Self(Box::new(re))
698 }
699}
700
701impl<'a> Display for EndsWith<'a> {
702 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
703 write!(f, "{}$", self.0)
704 }
705}
706
707impl_builder_from_iter!(EndsWith);
708
709/// Regex syntax for matching the pattern of the input regex at the start and end of the searched text.
710/// (That is, the pattern must match the complete searched text.)
711///
712/// ## Example
713///
714/// ```
715/// use readable_regex::solvers::StartsAndEndsWith;
716/// use readable_regex::ReadableRe::Raw;
717/// let query = StartsAndEndsWith::new(Raw("abc"));
718/// assert_eq!(query.to_string(), "^abc$");
719/// ```
720#[derive(Clone)]
721pub struct StartsAndEndsWith<'a>(Box<ReadableRe<'a>>);
722
723impl<'a> StartsAndEndsWith<'a> {
724 pub fn new(re: ReadableRe<'a>) -> Self {
725 Self(Box::new(ReadableRe::StartsWith(StartsWith::new(
726 ReadableRe::EndsWith(EndsWith::new(re)),
727 ))))
728 }
729}
730
731impl<'a> Display for StartsAndEndsWith<'a> {
732 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
733 write!(f, "{}", self.0)
734 }
735}
736
737impl_builder_from_iter!(StartsAndEndsWith);
738
739/// Regex syntax for a character class including the characters in the input [`IntoIterator`]
740///
741/// ## Example
742///
743/// ```
744/// use readable_regex::solvers::Chars;
745/// let query = Chars::new("abc");
746/// assert_eq!(query.to_string(), "[abc]");
747/// ```
748#[derive(Clone)]
749pub struct Chars(String);
750
751impl Chars {
752 pub fn new(s: &str) -> Self {
753 Self(s.to_string())
754 }
755}
756
757impl Display for Chars {
758 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
759 write!(f, "[{}]", self.0)
760 }
761}
762
763/// Regex syntax for a character class excluding the characters in the input [`IntoIterator`]
764///
765/// ## Example
766///
767/// ```
768/// use readable_regex::solvers::NotChars;
769/// let query = NotChars::new("abc");
770/// assert_eq!(query.to_string(), "[^abc]");
771/// ```
772#[derive(Clone)]
773pub struct NotChars(String);
774
775impl NotChars {
776 pub fn new(s: &str) -> Self {
777 Self(s.to_string())
778 }
779}
780
781impl Display for NotChars {
782 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
783 write!(f, "[^{}]", self.0)
784 }
785}
786
787#[cfg(feature = "re-fancy")]
788/// Regex syntax for an atomic group of the input regex
789///
790/// ## Example
791///
792/// ```
793/// use readable_regex::solvers::AtomicGroup;
794/// use readable_regex::ReadableRe::Raw;
795/// let query = AtomicGroup::new(Raw("foo"));
796/// assert_eq!(query.to_string(), "(?>foo)")
797/// ```
798#[derive(Clone)]
799pub struct AtomicGroup<'a>(Box<ReadableRe<'a>>);
800
801#[cfg(feature = "re-fancy")]
802impl<'a> AtomicGroup<'a> {
803 pub fn new(re: ReadableRe<'a>) -> Self {
804 Self(Box::new(re))
805 }
806}
807
808#[cfg(feature = "re-fancy")]
809impl<'a> Display for AtomicGroup<'a> {
810 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
811 write!(f, "(?>{})", self.0)
812 }
813}
814
815#[cfg(feature = "re-fancy")]
816impl_builder_from_iter!(AtomicGroup);