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);