Skip to content

Allow exec* methods to fail with partial matches. #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ Cadence Marseille <[email protected]>
Damien Schoof <[email protected]>
James Rowe <[email protected]>
Mikhail Borisov <[email protected]>
Matt Kline <[email protected]>
8 changes: 4 additions & 4 deletions examples/pcredemo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ fn main() {

let opt_m = re.exec(&subject);
let m = match opt_m {
None => {
Err(_) => {
println!("No match");
//env::set_exit_status(1);
return;
}
Some(m) => m
Ok(m) => m
};
print_match(&m, &name_table);

Expand All @@ -126,11 +126,11 @@ fn main() {
loop {
let opt_m = re.exec_from(&subject, start_offset);
let m = match opt_m {
None => {
Err(_) => {
println!("\nNo more matches");
return;
}
Some(m) => m
Ok(m) => m
};

println!("");
Expand Down
1 change: 1 addition & 0 deletions libpcre-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub const PCRE_NO_UTF8_CHECK: c_int = 0x00002000;

pub const PCRE_ERROR_NOMATCH: c_int = -1;
pub const PCRE_ERROR_NULL: c_int = -2;
pub const PCRE_ERROR_PARTIAL: c_int = -12;

pub const PCRE_INFO_CAPTURECOUNT: fullinfo_field = 2;
pub const PCRE_INFO_NAMEENTRYSIZE: fullinfo_field = 7;
Expand Down
4 changes: 2 additions & 2 deletions src/detail/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use enum_set::{EnumSet};
use libc::{c_char, c_int, c_uchar, c_void};
use libpcre_sys;
pub use libpcre_sys::{pcre, compile_options, exec_options, fullinfo_field, study_options, PCRE_UTF8, PCRE_NO_UTF8_CHECK, PCRE_ERROR_NOMATCH, PCRE_ERROR_NULL};
pub use libpcre_sys::{pcre, compile_options, exec_options, fullinfo_field, study_options, PCRE_UTF8, PCRE_NO_UTF8_CHECK, PCRE_ERROR_NOMATCH, PCRE_ERROR_NULL, PCRE_ERROR_PARTIAL};
use std::ffi::{CStr};
use std::ptr;
use std::result::{Result};
Expand Down Expand Up @@ -50,7 +50,7 @@ pub unsafe fn pcre_exec(code: *const pcre, extra: *const ::PcreExtra, subject: *
let rc = libpcre_sys::pcre_exec(code, extra, subject, length, startoffset, converted_options, ovector, ovecsize);
if rc == PCRE_ERROR_NOMATCH {
return -1;
} else if rc < 0 && rc != PCRE_ERROR_NULL {
} else if rc < 0 && rc != PCRE_ERROR_NULL && rc != PCRE_ERROR_PARTIAL {
panic!("pcre_exec");
}

Expand Down
29 changes: 23 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ pub enum ExecOption {
ExecNotEmptyAtStart = 0x10000000
}

/// In the general case, we don't care what the error was,
/// but if [exec_from_with_options()](struct.Pcre.html#method.exec_from_with_options)
/// was called with ExecPartialSoft or ExecPartialHard, we *do* want to know
/// if the call failed with a partial match.
#[derive(Clone, Debug)]
pub enum ExecError {
PartialMatch,
GeneralError
}

#[allow(non_upper_case_globals)]
pub const ExecPartial: ExecOption = ExecOption::ExecPartialSoft;
#[allow(non_upper_case_globals)]
Expand Down Expand Up @@ -120,6 +130,11 @@ pub struct Match<'a> {
}

/// Iterator type for iterating matches within a subject string.
///
/// Partial matches are unsupported, even if the iterator was created by
/// passing ExecPartialSoft or ExecPartialHard to
/// [matches_with_options()](struct.Pcre.html#method.matches_with_options),
/// since next() must return an `Option`, not a `Result`.
pub struct MatchIterator<'a, 'p> {

code: *const detail::pcre,
Expand Down Expand Up @@ -390,7 +405,7 @@ impl Pcre {
/// If a regular expression will be used often, it might be worth studying it to possibly
/// speed up matching. See the [study()](#method.study) method.
#[inline]
pub fn exec<'a, 'p>(&'p mut self, subject: &'a str) -> Option<Match<'a>> {
pub fn exec<'a, 'p>(&'p mut self, subject: &'a str) -> Result<Match<'a>, ExecError> {
self.exec_from(subject, 0)
}

Expand All @@ -411,7 +426,7 @@ impl Pcre {
/// If a regular expression will be used often, it might be worth studying it to possibly
/// speed up matching. See the [study()](#method.study) method.
#[inline]
pub fn exec_from<'a, 'p>(&'p mut self, subject: &'a str, startoffset: usize) -> Option<Match<'a>> {
pub fn exec_from<'a, 'p>(&'p mut self, subject: &'a str, startoffset: usize) -> Result<Match<'a>, ExecError> {
let no_options: EnumSet<ExecOption> = EnumSet::new();
self.exec_from_with_options(subject, startoffset, &no_options)
}
Expand All @@ -436,7 +451,7 @@ impl Pcre {
/// If a regular expression will be used often, it might be worth studying it to possibly
/// speed up matching. See the [study()](#method.study) method.
#[inline]
pub fn exec_from_with_options<'a, 'p>(&'p mut self, subject: &'a str, startoffset: usize, options: &EnumSet<ExecOption>) -> Option<Match<'a>> {
pub fn exec_from_with_options<'a, 'p>(&'p mut self, subject: &'a str, startoffset: usize, options: &EnumSet<ExecOption>) -> Result<Match<'a>, ExecError> {
let ovecsize = (self.capture_count_ + 1) * 3;
let mut ovector = vec![0 as c_int; ovecsize as usize];

Expand All @@ -450,13 +465,15 @@ impl Pcre {
ovector.as_mut_ptr(),
ovecsize as c_int);
if rc >= 0 {
Some(Match {
Ok(Match {
subject: subject,
partial_ovector: ovector[..(((self.capture_count_ + 1) * 2) as usize)].to_vec(),
string_count_: rc
string_count_: rc,
})
} else if rc == libpcre_sys::PCRE_ERROR_PARTIAL {
Err(ExecError::PartialMatch)
} else {
None
Err(ExecError::GeneralError)
}
}
}
Expand Down
22 changes: 18 additions & 4 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ extern crate enum_set;
extern crate pcre;

use enum_set::{EnumSet};
use pcre::{CompileOption, Pcre, StudyOption};
use pcre::{CompileOption, Pcre, StudyOption, ExecOption, ExecError};

#[test]
#[should_panic]
Expand Down Expand Up @@ -40,7 +40,7 @@ fn test_exec_basic() {
#[test]
fn test_exec_no_match() {
let mut re = Pcre::compile("abc").unwrap();
assert!(re.exec("def").is_none());
assert!(re.exec("def").is_err());
}

#[test]
Expand Down Expand Up @@ -148,7 +148,7 @@ fn test_extra_mark() {

// Now execute and we should be able to get the mark
let opt_m1 = re.exec(subject1);
assert!(opt_m1.is_some());
assert!(opt_m1.is_ok());

// It should match XY
let m1 = opt_m1.unwrap();
Expand All @@ -160,7 +160,7 @@ fn test_extra_mark() {
assert_eq!(mark1.unwrap(), "A");

let opt_m2 = re.exec(subject2);
assert!(opt_m2.is_some());
assert!(opt_m2.is_ok());

let m2 = opt_m2.unwrap();
// It should match XZ
Expand All @@ -181,3 +181,17 @@ fn test_optional_capture() {
assert_eq!(m1.group_start(1), usize::max_value()); // c_int -1
assert_eq!(m1.group_end(1), usize::max_value()); // c_int -1
}

#[test]
fn test_partial_capture() {
let mut re = Pcre::compile("foobar").unwrap();
let mut subject = "foo";
let mut opts : EnumSet<ExecOption> = EnumSet::new();
opts.insert(ExecOption::ExecPartialHard);
match re.exec_from_with_options(subject, 0, &opts) {
Err(ExecError::PartialMatch) => (),
_ => panic!("Partial match test failed")
}
subject = "foobar";
assert!(re.exec_from_with_options(subject, 0, &opts).is_ok());
}