@@ -8,7 +8,7 @@ use log::debug;
8
8
use pcre2_sys:: {
9
9
PCRE2_CASELESS , PCRE2_DOTALL , PCRE2_EXTENDED , PCRE2_MULTILINE ,
10
10
PCRE2_UCP , PCRE2_UTF , PCRE2_NO_UTF_CHECK , PCRE2_UNSET ,
11
- PCRE2_NEWLINE_ANYCRLF ,
11
+ PCRE2_NEWLINE_ANYCRLF , PCRE2_PARTIAL_HARD
12
12
} ;
13
13
use thread_local:: CachedThreadLocal ;
14
14
@@ -427,6 +427,25 @@ impl Regex {
427
427
self . is_match_at ( subject, 0 )
428
428
}
429
429
430
+ /// Returns true if and only if the regex fully or partially matches the subject string given.
431
+ /// A partial match occurs when there is a match up to the end of a subject string,
432
+ /// but more characters are needed to match the entire pattern.
433
+ ///
434
+ /// # Example
435
+ ///
436
+ /// Test if given string can be a beginning of a valid telephone number:
437
+ /// ```rust
438
+ /// # fn example() -> Result<(), ::pcre2::Error> {
439
+ /// use pcre2::bytes::Regex;
440
+ ///
441
+ /// let text = b"123-456-";
442
+ /// assert!(Regex::new(r"^\d{3}-\d{3}-\d{3}")?.is_partial_match(text)?);
443
+ /// # Ok(()) }; example().unwrap()
444
+ /// ```
445
+ pub fn is_partial_match ( & self , subject : & [ u8 ] ) -> Result < bool , Error > {
446
+ self . is_partial_match_at ( subject, 0 )
447
+ }
448
+
430
449
/// Returns the start and end byte range of the leftmost-first match in
431
450
/// `subject`. If no match exists, then `None` is returned.
432
451
///
@@ -628,6 +647,39 @@ impl Regex {
628
647
Ok ( unsafe { match_data. find ( & self . code , subject, start, options) ? } )
629
648
}
630
649
650
+ /// Returns the same as is_partial_match, but starts the search at the given
651
+ /// offset.
652
+ ///
653
+ /// The significance of the starting point is that it takes the surrounding
654
+ /// context into consideration. For example, the `\A` anchor can only
655
+ /// match when `start == 0`.
656
+ pub fn is_partial_match_at (
657
+ & self ,
658
+ subject : & [ u8 ] ,
659
+ start : usize ,
660
+ ) -> Result < bool , Error > {
661
+ assert ! (
662
+ start <= subject. len( ) ,
663
+ "start ({}) must be <= subject.len() ({})" ,
664
+ start,
665
+ subject. len( )
666
+ ) ;
667
+
668
+ let mut options = PCRE2_PARTIAL_HARD ;
669
+ if !self . config . utf_check {
670
+ options |= PCRE2_NO_UTF_CHECK ;
671
+ }
672
+
673
+ let match_data = self . match_data ( ) ;
674
+ let mut match_data = match_data. borrow_mut ( ) ;
675
+ // SAFETY: The only unsafe PCRE2 option we potentially use here is
676
+ // PCRE2_NO_UTF_CHECK, and that only occurs if the caller executes the
677
+ // `disable_utf_check` method, which propagates the safety contract to
678
+ // the caller.
679
+ Ok ( unsafe { match_data. find ( & self . code , subject, start, options) ? } )
680
+ }
681
+
682
+
631
683
/// Returns the same as find, but starts the search at the given
632
684
/// offset.
633
685
///
@@ -1150,6 +1202,18 @@ mod tests {
1150
1202
assert ! ( re. is_match( b( "Β" ) ) . unwrap( ) ) ;
1151
1203
}
1152
1204
1205
+ #[ test]
1206
+ fn partial ( ) {
1207
+ let re = RegexBuilder :: new ( )
1208
+ . build ( "ab$" )
1209
+ . unwrap ( ) ;
1210
+
1211
+ assert ! ( re. is_partial_match( b( "a" ) ) . unwrap( ) ) ;
1212
+ assert ! ( re. is_partial_match( b( "ab" ) ) . unwrap( ) ) ;
1213
+ assert ! ( !re. is_partial_match( b( "abc" ) ) . unwrap( ) ) ;
1214
+ assert ! ( !re. is_partial_match( b( "b" ) ) . unwrap( ) ) ;
1215
+ }
1216
+
1153
1217
#[ test]
1154
1218
fn crlf ( ) {
1155
1219
let re = RegexBuilder :: new ( )
0 commit comments