diff --git a/AUTHORS b/AUTHORS index 624cf02..0ca5978 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,3 +3,4 @@ Cadence Marseille Damien Schoof James Rowe Mikhail Borisov +Matt Kline diff --git a/examples/pcredemo.rs b/examples/pcredemo.rs index be25c5b..0e81b56 100644 --- a/examples/pcredemo.rs +++ b/examples/pcredemo.rs @@ -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); @@ -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!(""); diff --git a/libpcre-sys/src/lib.rs b/libpcre-sys/src/lib.rs index 65a89a9..de3cc8b 100644 --- a/libpcre-sys/src/lib.rs +++ b/libpcre-sys/src/lib.rs @@ -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; diff --git a/src/detail/mod.rs b/src/detail/mod.rs index 5777150..498011d 100644 --- a/src/detail/mod.rs +++ b/src/detail/mod.rs @@ -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}; @@ -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"); } diff --git a/src/lib.rs b/src/lib.rs index 9a8215b..f648684 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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)] @@ -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, @@ -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> { + pub fn exec<'a, 'p>(&'p mut self, subject: &'a str) -> Result, ExecError> { self.exec_from(subject, 0) } @@ -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> { + pub fn exec_from<'a, 'p>(&'p mut self, subject: &'a str, startoffset: usize) -> Result, ExecError> { let no_options: EnumSet = EnumSet::new(); self.exec_from_with_options(subject, startoffset, &no_options) } @@ -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) -> Option> { + pub fn exec_from_with_options<'a, 'p>(&'p mut self, subject: &'a str, startoffset: usize, options: &EnumSet) -> Result, ExecError> { let ovecsize = (self.capture_count_ + 1) * 3; let mut ovector = vec![0 as c_int; ovecsize as usize]; @@ -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) } } } diff --git a/tests/test.rs b/tests/test.rs index d14bdb6..0640d51 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -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] @@ -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] @@ -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(); @@ -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 @@ -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 = 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()); +}