Struct Input
pub struct Input<'h> { /* private fields */ }
The parameters for a regex search including the haystack to search.
It turns out that regex searches have a few parameters, and in most cases,
those parameters have defaults that work in the vast majority of cases.
This Input type exists to make that common case seamless while also
providing an avenue for changing the parameters of a search. In particular,
this type enables doing so without a combinatorial explosion of different
methods and/or superfluous parameters in the common cases.
An Input permits configuring the following things:
- Search only a substring of a haystack, while taking the broader context into account for resolving look-around assertions.
- Indicating whether to search for all patterns in a regex, or to only search for one pattern in particular.
- Whether to perform an anchored on unanchored search.
- Whether to report a match as early as possible.
All of these parameters, except for the haystack, have sensible default
values. This means that the minimal search configuration is simply a call
to Input::new with your haystack. Setting any other parameter is
optional.
Moreover, for any H that implements AsRef<[u8]>, there exists a
From<H> for Input implementation. This is useful because many of the
search APIs in this crate accept an Into<Input>. This means you can
provide string or byte strings to these routines directly, and they'll
automatically get converted into an Input for you.
The lifetime parameter 'h refers to the lifetime of the haystack.
Organization
The API of Input is split into a few different parts:
- A builder-like API that transforms a
Inputby value. Examples:Input::spanandInput::anchored. - A setter API that permits mutating parameters in place. Examples:
Input::set_spanandInput::set_anchored. - A getter API that permits retrieving any of the search parameters.
Examples:
Input::get_spanandInput::get_anchored. - A few convenience getter routines that don't conform to the above naming
pattern due to how common they are. Examples:
Input::haystack,Input::startandInput::end. - Miscellaneous predicates and other helper routines that are useful
in some contexts. Examples:
Input::is_char_boundary.
A Input exposes so much because it is meant to be used by both callers of
regex engines and implementors of regex engines. A constraining factor is
that regex engines should accept a &Input as its lowest level API, which
means that implementors should only use the "getter" APIs of a Input.
Valid bounds and search termination
An Input permits setting the bounds of a search via either
Input::span or Input::range. The bounds set must be valid, or
else a panic will occur. Bounds are valid if and only if:
- The bounds represent a valid range into the input's haystack.
- or the end bound is a valid ending bound for the haystack and the start bound is exactly one greater than the start bound.
In the latter case, Input::is_done will return true and indicates any
search receiving such an input should immediately return with no match.
Note that while Input is used for reverse searches in this crate, the
Input::is_done predicate assumes a forward search. Because unsigned
offsets are used internally, there is no way to tell from only the offsets
whether a reverse search is done or not.
Regex engine support
Any regex engine accepting an Input must support at least the following
things:
- Searching a
&[u8]for matches. - Searching a substring of
&[u8]for a match, such that any match reported must appear entirely within that substring. - For a forwards search, a match should never be reported when
Input::is_donereturns true. (For reverse searches, termination should be handled outside ofInput.)
Supporting other aspects of an Input are optional, but regex engines
should handle aspects they don't support gracefully. How this is done is
generally up to the regex engine. This crate generally treats unsupported
anchored modes as an error to report for example, but for simplicity, in
the meta regex engine, trying to search with an invalid pattern ID just
results in no match being reported.
Implementations
impl<'h> Input<'h>
fn new<H: ?Sized + AsRef<[u8]>>(haystack: &'h H) -> Input<'h>Create a new search configuration for the given haystack.
fn span<S: Into<Span>>(self, span: S) -> Input<'h>Set the span for this search.
This routine does not panic if the span given is not a valid range for this search's haystack. If this search is run with an invalid range, then the most likely outcome is that the actual search execution will panic.
This routine is generic over how a span is provided. While a
Spanmay be given directly, one may also provide astd::ops::Range<usize>. To provide anything supported by range syntax, use theInput::rangemethod.The default span is the entire haystack.
Note that
Input::rangeoverrides this method and vice versa.Panics
This panics if the given span does not correspond to valid bounds in the haystack or the termination of a search.
Example
This example shows how the span of the search can impact whether a match is reported or not. This is particularly relevant for look-around operators, which might take things outside of the span into account when determining whether they match.
# if cfg! // miri takes too long use ; // Look for 'at', but as a distinct word. let re = new?; let mut cache = re.create_cache; let mut caps = re.create_captures; // Our haystack contains 'at', but not as a distinct word. let haystack = "batter"; // A standard search finds nothing, as expected. let input = new; re.search; assert_eq!; // But if we wanted to search starting at position '1', we might // slice the haystack. If we do this, it's impossible for the \b // anchors to take the surrounding context into account! And thus, // a match is produced. let input = new; re.search; assert_eq!; // But if we specify the span of the search instead of slicing the // haystack, then the regex engine can "see" outside of the span // and resolve the anchors correctly. let input = new.span; re.search; assert_eq!; # Ok::This may seem a little ham-fisted, but this scenario tends to come up if some other regex engine found the match span and now you need to re-process that span to look for capturing groups. (e.g., Run a faster DFA first, find a match, then run the PikeVM on just the match span to resolve capturing groups.) In order to implement that sort of logic correctly, you need to set the span on the search instead of slicing the haystack directly.
The other advantage of using this routine to specify the bounds of the search is that the match offsets are still reported in terms of the original haystack. For example, the second search in the example above reported a match at position
0, even thoughatstarts at offset1because we sliced the haystack.fn range<R: RangeBounds<usize>>(self, range: R) -> Input<'h>Like
Input::span, but accepts any range instead.This routine does not panic if the range given is not a valid range for this search's haystack. If this search is run with an invalid range, then the most likely outcome is that the actual search execution will panic.
The default range is the entire haystack.
Note that
Input::spanoverrides this method and vice versa.Panics
This routine will panic if the given range could not be converted to a valid
Range. For example, this would panic when given0..=usize::MAXsince it cannot be represented using a half-open interval in terms ofusize.This also panics if the given range does not correspond to valid bounds in the haystack or the termination of a search.
Example
use Input; let input = new; assert_eq!; let input = new.range; assert_eq!;fn anchored(self, mode: Anchored) -> Input<'h>Sets the anchor mode of a search.
When a search is anchored (so that's
Anchored::YesorAnchored::Pattern), a match must begin at the start of a search. When a search is not anchored (that'sAnchored::No), regex engines will behave as if the pattern started with a(?s-u:.)*?. This prefix permits a match to appear anywhere.By default, the anchored mode is
Anchored::No.WARNING: this is subtly different than using a
^at the start of your regex. A^forces a regex to match exclusively at the start of a haystack, regardless of where you begin your search. In contrast, anchoring a search will allow your regex to match anywhere in your haystack, but the match must start at the beginning of a search.For example, consider the haystack
abaand the following searches:- The regex
^ais compiled withAnchored::Noand searchesabastarting at position2. Since^requires the match to start at the beginning of the haystack and2 > 0, no match is found. - The regex
ais compiled withAnchored::Yesand searchesabastarting at position2. This reports a match at[2, 3]since the match starts where the search started. Since there is no^, there is no requirement for the match to start at the beginning of the haystack. - The regex
ais compiled withAnchored::Yesand searchesabastarting at position1. Sincebcorresponds to position1and since the search is anchored, it finds no match. While the regex matches at other positions, configuring the search to be anchored requires that it only report a match that begins at the same offset as the beginning of the search. - The regex
ais compiled withAnchored::Noand searchesabastarting at position1. Since the search is not anchored and the regex does not start with^, the search executes as if there is a(?s:.)*?prefix that permits it to match anywhere. Thus, it reports a match at[2, 3].
Note that the
Anchored::Patternmode is likeAnchored::Yes, except it only reports matches for a particular pattern.Example
This demonstrates the differences between an anchored search and a pattern that begins with
^(as described in the above warning message).use ; let haystack = "aba"; let re = new?; let = ; let input = new.span.anchored; re.search; // No match is found because 2 is not the beginning of the haystack, // which is what ^ requires. assert_eq!; let re = new?; let = ; let input = new.span.anchored; re.search; // An anchored search can still match anywhere in the haystack, it just // must begin at the start of the search which is '2' in this case. assert_eq!; let re = new?; let = ; let input = new.span.anchored; re.search; // No match is found since we start searching at offset 1 which // corresponds to 'b'. Since there is no '(?s:.)*?' prefix, no match // is found. assert_eq!; let re = new?; let = ; let input = new.span.anchored; re.search; // Since anchored=no, an implicit '(?s:.)*?' prefix was added to the // pattern. Even though the search starts at 'b', the 'match anything' // prefix allows the search to match 'a'. let expected = Some; assert_eq!; # Ok::- The regex
fn earliest(self, yes: bool) -> Input<'h>Whether to execute an "earliest" search or not.
When running a non-overlapping search, an "earliest" search will return the match location as early as possible. For example, given a pattern of
foo[0-9]+and a haystack offoo12345, a normal leftmost search will returnfoo12345as a match. But an "earliest" search for regex engines that support "earliest" semantics will returnfoo1as a match, since as soon as the first digit followingfoois seen, it is known to have found a match.Note that "earliest" semantics generally depend on the regex engine. Different regex engines may determine there is a match at different points. So there is no guarantee that "earliest" matches will always return the same offsets for all regex engines. The "earliest" notion is really about when the particular regex engine determines there is a match rather than a consistent semantic unto itself. This is often useful for implementing "did a match occur or not" predicates, but sometimes the offset is useful as well.
This is disabled by default.
Example
This example shows the difference between "earliest" searching and normal searching.
use ; let re = new?; let mut cache = re.create_cache; let mut caps = re.create_captures; // A normal search implements greediness like you expect. let input = new; re.search; assert_eq!; // When 'earliest' is enabled and the regex engine supports // it, the search will bail once it knows a match has been // found. let input = new.earliest; re.search; assert_eq!; # Ok::fn set_span<S: Into<Span>>(&mut self, span: S)Set the span for this search configuration.
This is like the
Input::spanmethod, except this mutates the span in place.This routine is generic over how a span is provided. While a
Spanmay be given directly, one may also provide astd::ops::Range<usize>.Panics
This panics if the given span does not correspond to valid bounds in the haystack or the termination of a search.
Example
use Input; let mut input = new; assert_eq!; input.set_span; assert_eq!;fn set_range<R: RangeBounds<usize>>(&mut self, range: R)Set the span for this search configuration given any range.
This is like the
Input::rangemethod, except this mutates the span in place.This routine does not panic if the range given is not a valid range for this search's haystack. If this search is run with an invalid range, then the most likely outcome is that the actual search execution will panic.
Panics
This routine will panic if the given range could not be converted to a valid
Range. For example, this would panic when given0..=usize::MAXsince it cannot be represented using a half-open interval in terms ofusize.This also panics if the given span does not correspond to valid bounds in the haystack or the termination of a search.
Example
use Input; let mut input = new; assert_eq!; input.set_range; assert_eq!;fn set_start(&mut self, start: usize)Set the starting offset for the span for this search configuration.
This is a convenience routine for only mutating the start of a span without having to set the entire span.
Panics
This panics if the span resulting from the new start position does not correspond to valid bounds in the haystack or the termination of a search.
Example
use Input; let mut input = new; assert_eq!; input.set_start; assert_eq!;fn set_end(&mut self, end: usize)Set the ending offset for the span for this search configuration.
This is a convenience routine for only mutating the end of a span without having to set the entire span.
Panics
This panics if the span resulting from the new end position does not correspond to valid bounds in the haystack or the termination of a search.
Example
use Input; let mut input = new; assert_eq!; input.set_end; assert_eq!;fn set_anchored(&mut self, mode: Anchored)Set the anchor mode of a search.
This is like
Input::anchored, except it mutates the search configuration in place.Example
use ; let mut input = new; assert_eq!; let pid = must; input.set_anchored; assert_eq!;fn set_earliest(&mut self, yes: bool)Set whether the search should execute in "earliest" mode or not.
This is like
Input::earliest, except it mutates the search configuration in place.Example
use Input; let mut input = new; assert!; input.set_earliest; assert!;fn haystack(&self) -> &'h [u8]Return a borrow of the underlying haystack as a slice of bytes.
Example
use Input; let input = new; assert_eq!;fn start(&self) -> usizeReturn the start position of this search.
This is a convenience routine for
search.get_span().start().When
Input::is_doneisfalse, this is guaranteed to return an offset that is less than or equal toInput::end. Otherwise, the offset is one greater thanInput::end.Example
use Input; let input = new; assert_eq!; let input = new.span; assert_eq!;fn end(&self) -> usizeReturn the end position of this search.
This is a convenience routine for
search.get_span().end().This is guaranteed to return an offset that is a valid exclusive end bound for this input's haystack.
Example
use Input; let input = new; assert_eq!; let input = new.span; assert_eq!;fn get_span(&self) -> SpanReturn the span for this search configuration.
If one was not explicitly set, then the span corresponds to the entire range of the haystack.
When
Input::is_doneisfalse, the span returned is guaranteed to correspond to valid bounds for this input's haystack.Example
use ; let input = new; assert_eq!;fn get_range(&self) -> Range<usize>Return the span as a range for this search configuration.
If one was not explicitly set, then the span corresponds to the entire range of the haystack.
When
Input::is_doneisfalse, the range returned is guaranteed to correspond to valid bounds for this input's haystack.Example
use Input; let input = new; assert_eq!;fn get_anchored(&self) -> AnchoredReturn the anchored mode for this search configuration.
If no anchored mode was set, then it defaults to
Anchored::No.Example
use ; let mut input = new; assert_eq!; let pid = must; input.set_anchored; assert_eq!;fn get_earliest(&self) -> boolReturn whether this search should execute in "earliest" mode.
Example
use Input; let input = new; assert!;fn is_done(&self) -> boolReturn true if and only if this search can never return any other matches.
This occurs when the start position of this search is greater than the end position of the search.
Example
use Input; let mut input = new; assert!; input.set_start; assert!; input.set_start; assert!;fn is_char_boundary(&self, offset: usize) -> boolReturns true if and only if the given offset in this search's haystack falls on a valid UTF-8 encoded codepoint boundary.
If the haystack is not valid UTF-8, then the behavior of this routine is unspecified.
Example
This shows where codepoint boundaries do and don't exist in valid UTF-8.
use Input; let input = new; assert!; assert!; assert!; assert!; assert!;
Trait Implementations
impl<'h> Clone for Input<'h>
fn clone(&self) -> Self
impl<'h> Debug for Input<'h>
fn fmt(&self, f: &mut Formatter<'_>) -> Result
impl<'h, H: ?Sized + AsRef<[u8]>> From<&'h H> for Input<'h>
fn from(haystack: &'h H) -> Input<'h>
Auto Trait Implementations
impl<'h> Freeze for Input<'h>
impl<'h> RefUnwindSafe for Input<'h>
impl<'h> Send for Input<'h>
impl<'h> Sync for Input<'h>
impl<'h> Unpin for Input<'h>
impl<'h> UnsafeUnpin for Input<'h>
impl<'h> UnwindSafe for Input<'h>
Blanket Implementations
impl<T> Any for Input<'h>
where
T: 'static + ?Sized,
fn type_id(&self) -> TypeId
impl<T> Borrow<T> for Input<'h>
where
T: ?Sized,
fn borrow(&self) -> &T
impl<T> BorrowMut<T> for Input<'h>
where
T: ?Sized,
fn borrow_mut(&mut self) -> &mut T
impl<T> CloneToUninit for Input<'h>
where
T: Clone,
unsafe fn clone_to_uninit(&self, dest: *mut u8)
impl<T> From<T> for Input<'h>
fn from(t: T) -> TReturns the argument unchanged.
impl<T> ToOwned for Input<'h>
where
T: Clone,
type Owned = T;fn to_owned(&self) -> Tfn clone_into(&self, target: &mut T)
impl<T, U> Into<U> for Input<'h>
where
U: From<T>,
fn into(self) -> UCalls
U::from(self).That is, this conversion is whatever the implementation of
[From]<T> for Uchooses to do.
impl<T, U> TryFrom<U> for Input<'h>
where
U: Into<T>,
type Error = never;fn try_from(value: U) -> Result<T, never>
impl<T, U> TryInto<U> for Input<'h>
where
U: TryFrom<T>,
type Error = <U as TryFrom<T>>::Error;fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>