diff options
| author | Eric Kidd <git@randomhacks.net> | 2014-12-13 13:33:18 -0500 |
|---|---|---|
| committer | Eric Kidd <git@randomhacks.net> | 2014-12-14 08:56:51 -0500 |
| commit | c2b0d7dd8818a0dca9b1fa7af6873375907f05ca (patch) | |
| tree | 369f80679f73646e73ed5f58b2041eb8183aa242 /src/libregex | |
| parent | 444fa1b7cffcd99ca5b8abb51acf979f06a25899 (diff) | |
| download | rust-c2b0d7dd8818a0dca9b1fa7af6873375907f05ca.tar.gz rust-c2b0d7dd8818a0dca9b1fa7af6873375907f05ca.zip | |
Modify `regex::Captures::{at,name}` to return `Option`
Closes #14602. As discussed in that issue, the existing `at` and `name`
functions represent two different results with the empty string:
1. Matched the empty string.
2. Did not match anything.
Consider the following example. This regex has two named matched
groups, `key` and `value`. `value` is optional:
```rust
// Matches "foo", "foo;v=bar" and "foo;v=".
regex!(r"(?P<key>[a-z]+)(;v=(?P<value>[a-z]*))?");
```
We can access `value` using `caps.name("value")`, but there's no way for
us to distinguish between the `"foo"` and `"foo;v="` cases.
Early this year, @BurntSushi recommended modifying the existing `at` and
`name` functions to return `Option`, instead of adding new functions to
the API.
This is a [breaking-change], but the fix is easy:
- `refs.at(1)` becomes `refs.at(1).unwrap_or("")`.
- `refs.name(name)` becomes `refs.name(name).unwrap_or("")`.
Diffstat (limited to 'src/libregex')
| -rw-r--r-- | src/libregex/lib.rs | 6 | ||||
| -rw-r--r-- | src/libregex/re.rs | 49 |
2 files changed, 28 insertions, 27 deletions
diff --git a/src/libregex/lib.rs b/src/libregex/lib.rs index 05f853a851e..3fadba9583e 100644 --- a/src/libregex/lib.rs +++ b/src/libregex/lib.rs @@ -103,7 +103,9 @@ //! let re = regex!(r"(\d{4})-(\d{2})-(\d{2})"); //! let text = "2012-03-14, 2013-01-01 and 2014-07-05"; //! for cap in re.captures_iter(text) { -//! println!("Month: {} Day: {} Year: {}", cap.at(2), cap.at(3), cap.at(1)); +//! println!("Month: {} Day: {} Year: {}", +//! cap.at(2).unwrap_or(""), cap.at(3).unwrap_or(""), +//! cap.at(1).unwrap_or("")); //! } //! // Output: //! // Month: 03 Day: 14 Year: 2012 @@ -285,7 +287,7 @@ //! # fn main() { //! let re = regex!(r"(?i)a+(?-i)b+"); //! let cap = re.captures("AaAaAbbBBBb").unwrap(); -//! assert_eq!(cap.at(0), "AaAaAbb"); +//! assert_eq!(cap.at(0), Some("AaAaAbb")); //! # } //! ``` //! diff --git a/src/libregex/re.rs b/src/libregex/re.rs index 1504e191985..53181bfbb7e 100644 --- a/src/libregex/re.rs +++ b/src/libregex/re.rs @@ -273,9 +273,9 @@ impl Regex { /// let re = regex!(r"'([^']+)'\s+\((\d{4})\)"); /// let text = "Not my favorite movie: 'Citizen Kane' (1941)."; /// let caps = re.captures(text).unwrap(); - /// assert_eq!(caps.at(1), "Citizen Kane"); - /// assert_eq!(caps.at(2), "1941"); - /// assert_eq!(caps.at(0), "'Citizen Kane' (1941)"); + /// assert_eq!(caps.at(1), Some("Citizen Kane")); + /// assert_eq!(caps.at(2), Some("1941")); + /// assert_eq!(caps.at(0), Some("'Citizen Kane' (1941)")); /// # } /// ``` /// @@ -291,9 +291,9 @@ impl Regex { /// let re = regex!(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)"); /// let text = "Not my favorite movie: 'Citizen Kane' (1941)."; /// let caps = re.captures(text).unwrap(); - /// assert_eq!(caps.name("title"), "Citizen Kane"); - /// assert_eq!(caps.name("year"), "1941"); - /// assert_eq!(caps.at(0), "'Citizen Kane' (1941)"); + /// assert_eq!(caps.name("title"), Some("Citizen Kane")); + /// assert_eq!(caps.name("year"), Some("1941")); + /// assert_eq!(caps.at(0), Some("'Citizen Kane' (1941)")); /// # } /// ``` /// @@ -434,7 +434,7 @@ impl Regex { /// # use regex::Captures; fn main() { /// let re = regex!(r"([^,\s]+),\s+(\S+)"); /// let result = re.replace("Springsteen, Bruce", |&: caps: &Captures| { - /// format!("{} {}", caps.at(2), caps.at(1)) + /// format!("{} {}", caps.at(2).unwrap_or(""), caps.at(1).unwrap_or("")) /// }); /// assert_eq!(result.as_slice(), "Bruce Springsteen"); /// # } @@ -712,27 +712,25 @@ impl<'t> Captures<'t> { Some((self.locs[s].unwrap(), self.locs[e].unwrap())) } - /// Returns the matched string for the capture group `i`. - /// If `i` isn't a valid capture group or didn't match anything, then the - /// empty string is returned. - pub fn at(&self, i: uint) -> &'t str { + /// Returns the matched string for the capture group `i`. If `i` isn't + /// a valid capture group or didn't match anything, then `None` is + /// returned. + pub fn at(&self, i: uint) -> Option<&'t str> { match self.pos(i) { - None => "", - Some((s, e)) => { - self.text.slice(s, e) - } + None => None, + Some((s, e)) => Some(self.text.slice(s, e)) } } - /// Returns the matched string for the capture group named `name`. - /// If `name` isn't a valid capture group or didn't match anything, then - /// the empty string is returned. - pub fn name(&self, name: &str) -> &'t str { + /// Returns the matched string for the capture group named `name`. If + /// `name` isn't a valid capture group or didn't match anything, then + /// `None` is returned. + pub fn name(&self, name: &str) -> Option<&'t str> { match self.named { - None => "", + None => None, Some(ref h) => { match h.get(name) { - None => "", + None => None, Some(i) => self.at(*i), } } @@ -769,11 +767,12 @@ impl<'t> Captures<'t> { // FIXME: Don't use regexes for this. It's completely unnecessary. let re = Regex::new(r"(^|[^$]|\b)\$(\w+)").unwrap(); let text = re.replace_all(text, |&mut: refs: &Captures| -> String { - let (pre, name) = (refs.at(1), refs.at(2)); + let pre = refs.at(1).unwrap_or(""); + let name = refs.at(2).unwrap_or(""); format!("{}{}", pre, match from_str::<uint>(name.as_slice()) { - None => self.name(name).to_string(), - Some(i) => self.at(i).to_string(), + None => self.name(name).unwrap_or("").to_string(), + Some(i) => self.at(i).unwrap_or("").to_string(), }) }); let re = Regex::new(r"\$\$").unwrap(); @@ -802,7 +801,7 @@ impl<'t> Iterator<&'t str> for SubCaptures<'t> { fn next(&mut self) -> Option<&'t str> { if self.idx < self.caps.len() { self.idx += 1; - Some(self.caps.at(self.idx - 1)) + Some(self.caps.at(self.idx - 1).unwrap_or("")) } else { None } |
