use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
use std::ops::Index;
use std::str::FromStr;
use std::sync::Arc;
use memchr::memchr;
use syntax;
use error::Error;
use exec::{Exec, ExecNoSyncStr};
use expand::expand_str;
use re_builder::unicode::RegexBuilder;
use re_plugin::Plugin;
use re_trait::{self, RegularExpression, Locations, SubCapturesPosIter};
pub fn escape(text: &str) -> String {
syntax::escape(text)
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Match<'t> {
text: &'t str,
start: usize,
end: usize,
}
impl<'t> Match<'t> {
#[inline]
pub fn start(&self) -> usize {
self.start
}
#[inline]
pub fn end(&self) -> usize {
self.end
}
#[inline]
pub fn as_str(&self) -> &'t str {
&self.text[self.start..self.end]
}
#[inline]
fn new(haystack: &'t str, start: usize, end: usize) -> Match<'t> {
Match {
text: haystack,
start: start,
end: end,
}
}
}
impl<'t> From<Match<'t>> for &'t str {
fn from(m: Match<'t>) -> &'t str {
m.as_str()
}
}
#[derive(Clone)]
pub struct Regex(#[doc(hidden)] pub _Regex);
#[derive(Clone)]
#[doc(hidden)]
pub enum _Regex {
#[doc(hidden)]
Dynamic(Exec),
#[doc(hidden)]
Plugin(Plugin),
}
impl fmt::Display for Regex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl fmt::Debug for Regex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
#[doc(hidden)]
impl From<Exec> for Regex {
fn from(exec: Exec) -> Regex {
Regex(_Regex::Dynamic(exec))
}
}
impl FromStr for Regex {
type Err = Error;
fn from_str(s: &str) -> Result<Regex, Error> {
Regex::new(s)
}
}
impl Regex {
pub fn new(re: &str) -> Result<Regex, Error> {
RegexBuilder::new(re).build()
}
pub fn is_match(&self, text: &str) -> bool {
self.is_match_at(text, 0)
}
pub fn find<'t>(&self, text: &'t str) -> Option<Match<'t>> {
self.find_at(text, 0)
}
pub fn find_iter<'r, 't>(&'r self, text: &'t str) -> Matches<'r, 't> {
match self.0 {
_Regex::Dynamic(ref exec) => {
let it = exec.searcher_str().find_iter(text);
Matches(MatchesInner::Dynamic(it))
}
_Regex::Plugin(ref plug) => {
let it = plug.find_iter(text);
Matches(MatchesInner::Plugin(it))
}
}
}
pub fn captures<'t>(&self, text: &'t str) -> Option<Captures<'t>> {
let mut locs = self.locations();
self.read_captures_at(&mut locs, text, 0).map(|_| Captures {
text: text,
locs: locs,
named_groups: NamedGroups::from_regex(self)
})
}
pub fn captures_iter<'r, 't>(
&'r self,
text: &'t str,
) -> CaptureMatches<'r, 't> {
match self.0 {
_Regex::Dynamic(ref exec) => {
let it = exec.searcher_str().captures_iter(text);
CaptureMatches(CaptureMatchesInner::Dynamic(it))
}
_Regex::Plugin(ref plug) => {
let it = plug.captures_iter(text);
CaptureMatches(CaptureMatchesInner::Plugin(it))
}
}
}
pub fn split<'r, 't>(&'r self, text: &'t str) -> Split<'r, 't> {
Split {
finder: self.find_iter(text),
last: 0,
}
}
pub fn splitn<'r, 't>(&'r self, text: &'t str, limit: usize)
-> SplitN<'r, 't> {
SplitN {
splits: self.split(text),
n: limit,
}
}
pub fn replace<'t, R: Replacer>(
&self,
text: &'t str,
rep: R,
) -> Cow<'t, str> {
self.replacen(text, 1, rep)
}
pub fn replace_all<'t, R: Replacer>(
&self,
text: &'t str,
rep: R,
) -> Cow<'t, str> {
self.replacen(text, 0, rep)
}
pub fn replacen<'t, R: Replacer>(
&self,
text: &'t str,
limit: usize,
mut rep: R,
) -> Cow<'t, str> {
if let Some(rep) = rep.no_expansion() {
let mut it = self.find_iter(text).enumerate().peekable();
if it.peek().is_none() {
return Cow::Borrowed(text);
}
let mut new = String::with_capacity(text.len());
let mut last_match = 0;
for (i, m) in it {
if limit > 0 && i >= limit {
break
}
new.push_str(&text[last_match..m.start()]);
new.push_str(&rep);
last_match = m.end();
}
new.push_str(&text[last_match..]);
return Cow::Owned(new);
}
let mut it = self.captures_iter(text).enumerate().peekable();
if it.peek().is_none() {
return Cow::Borrowed(text);
}
let mut new = String::with_capacity(text.len());
let mut last_match = 0;
for (i, cap) in it {
if limit > 0 && i >= limit {
break
}
let m = cap.get(0).unwrap();
new.push_str(&text[last_match..m.start()]);
rep.replace_append(&cap, &mut new);
last_match = m.end();
}
new.push_str(&text[last_match..]);
Cow::Owned(new)
}
}
impl Regex {
pub fn shortest_match(&self, text: &str) -> Option<usize> {
self.shortest_match_at(text, 0)
}
#[doc(hidden)]
pub fn shortest_match_at(
&self,
text: &str,
start: usize,
) -> Option<usize> {
match self.0 {
_Regex::Dynamic(ref exec) => {
exec.searcher_str().shortest_match_at(text, start)
}
_Regex::Plugin(ref plug) => plug.shortest_match_at(text, start),
}
}
#[doc(hidden)]
pub fn is_match_at(&self, text: &str, start: usize) -> bool {
self.shortest_match_at(text, start).is_some()
}
#[doc(hidden)]
pub fn find_at<'t>(
&self,
text: &'t str,
start: usize,
) -> Option<Match<'t>> {
match self.0 {
_Regex::Dynamic(ref exec) => {
exec.searcher_str().find_at(text, start).map(|(s, e)| {
Match::new(text, s, e)
})
}
_Regex::Plugin(ref plug) => {
plug.find_at(text, start).map(|(s, e)| Match::new(text, s, e))
}
}
}
#[doc(hidden)]
pub fn read_captures_at<'t>(
&self,
locs: &mut Locations,
text: &'t str,
start: usize,
) -> Option<Match<'t>> {
match self.0 {
_Regex::Dynamic(ref exec) => {
exec.searcher_str().read_captures_at(locs, text, start)
.map(|(s, e)| Match::new(text, s, e))
}
_Regex::Plugin(ref plug) => {
plug.read_captures_at(locs, text, start)
.map(|(s, e)| Match::new(text, s, e))
}
}
}
}
impl Regex {
pub fn as_str(&self) -> &str {
match self.0 {
_Regex::Dynamic(ref exec) => &exec.regex_strings()[0],
_Regex::Plugin(ref plug) => plug.original,
}
}
pub fn capture_names(&self) -> CaptureNames {
CaptureNames(match self.0 {
_Regex::Plugin(ref n) => _CaptureNames::Plugin(n.names.iter()),
_Regex::Dynamic(ref d) => {
_CaptureNames::Dynamic(d.capture_names().iter())
}
})
}
pub fn captures_len(&self) -> usize {
match self.0 {
_Regex::Plugin(ref n) => n.names.len(),
_Regex::Dynamic(ref d) => d.capture_names().len()
}
}
#[doc(hidden)]
pub fn locations(&self) -> Locations {
match self.0 {
_Regex::Dynamic(ref exec) => {
exec.searcher_str().locations()
}
_Regex::Plugin(ref plug) => plug.locations(),
}
}
}
pub struct CaptureNames<'r>(_CaptureNames<'r>);
enum _CaptureNames<'r> {
Plugin(::std::slice::Iter<'r, Option<&'static str>>),
Dynamic(::std::slice::Iter<'r, Option<String>>)
}
impl<'r> Iterator for CaptureNames<'r> {
type Item = Option<&'r str>;
fn next(&mut self) -> Option<Option<&'r str>> {
match self.0 {
_CaptureNames::Plugin(ref mut i) => i.next().cloned(),
_CaptureNames::Dynamic(ref mut i) => {
i.next().as_ref().map(|o| o.as_ref().map(|s| s.as_ref()))
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 {
_CaptureNames::Plugin(ref i) => i.size_hint(),
_CaptureNames::Dynamic(ref i) => i.size_hint(),
}
}
}
pub struct Split<'r, 't> {
finder: Matches<'r, 't>,
last: usize,
}
impl<'r, 't> Iterator for Split<'r, 't> {
type Item = &'t str;
fn next(&mut self) -> Option<&'t str> {
let text = self.finder.text();
match self.finder.next() {
None => {
if self.last >= text.len() {
None
} else {
let s = &text[self.last..];
self.last = text.len();
Some(s)
}
}
Some(m) => {
let matched = &text[self.last..m.start()];
self.last = m.end();
Some(matched)
}
}
}
}
pub struct SplitN<'r, 't> {
splits: Split<'r, 't>,
n: usize,
}
impl<'r, 't> Iterator for SplitN<'r, 't> {
type Item = &'t str;
fn next(&mut self) -> Option<&'t str> {
if self.n == 0 {
return None
}
self.n -= 1;
if self.n == 0 {
let text = self.splits.finder.text();
Some(&text[self.splits.last..])
} else {
self.splits.next()
}
}
}
enum NamedGroups {
Plugin(&'static [(&'static str, usize)]),
Dynamic(Arc<HashMap<String, usize>>),
}
impl NamedGroups {
fn from_regex(regex: &Regex) -> NamedGroups {
match regex.0 {
_Regex::Plugin(ref plug) => NamedGroups::Plugin(plug.groups),
_Regex::Dynamic(ref exec) => {
NamedGroups::Dynamic(exec.capture_name_idx().clone())
}
}
}
fn pos(&self, name: &str) -> Option<usize> {
match *self {
NamedGroups::Plugin(groups) => {
groups.binary_search_by(|&(n, _)| n.cmp(name))
.ok().map(|i| groups[i].1)
},
NamedGroups::Dynamic(ref groups) => {
groups.get(name).cloned()
},
}
}
fn iter(& self) -> NamedGroupsIter {
match *self {
NamedGroups::Plugin(g) => NamedGroupsIter::Plugin(g.iter()),
NamedGroups::Dynamic(ref g) => NamedGroupsIter::Dynamic(g.iter()),
}
}
}
enum NamedGroupsIter<'n> {
Plugin(::std::slice::Iter<'static, (&'static str, usize)>),
Dynamic(::std::collections::hash_map::Iter<'n, String, usize>),
}
impl<'n> Iterator for NamedGroupsIter<'n> {
type Item = (&'n str, usize);
fn next(&mut self) -> Option<Self::Item> {
match *self {
NamedGroupsIter::Plugin(ref mut it) => it.next().cloned(),
NamedGroupsIter::Dynamic(ref mut it) => {
it.next().map(|(s, i)| (s.as_ref(), *i))
}
}
}
}
pub struct Captures<'t> {
text: &'t str,
locs: Locations,
named_groups: NamedGroups,
}
impl<'t> Captures<'t> {
pub fn get(&self, i: usize) -> Option<Match<'t>> {
self.locs.pos(i).map(|(s, e)| Match::new(self.text, s, e))
}
pub fn name(&self, name: &str) -> Option<Match<'t>> {
self.named_groups.pos(name).and_then(|i| self.get(i))
}
pub fn iter<'c>(&'c self) -> SubCaptureMatches<'c, 't> {
SubCaptureMatches {
caps: self,
it: self.locs.iter(),
}
}
pub fn expand(&self, replacement: &str, dst: &mut String) {
expand_str(self, replacement, dst)
}
#[inline]
pub fn len(&self) -> usize {
self.locs.len()
}
}
impl<'t> fmt::Debug for Captures<'t> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Captures").field(&CapturesDebug(self)).finish()
}
}
struct CapturesDebug<'c, 't: 'c>(&'c Captures<'t>);
impl<'c, 't> fmt::Debug for CapturesDebug<'c, 't> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let slot_to_name: HashMap<usize, &str> =
self.0.named_groups.iter().map(|(a, b)| (b, a)).collect();
let mut map = f.debug_map();
for (slot, m) in self.0.locs.iter().enumerate() {
let m = m.map(|(s, e)| &self.0.text[s..e]);
if let Some(name) = slot_to_name.get(&slot) {
map.entry(&name, &m);
} else {
map.entry(&slot, &m);
}
}
map.finish()
}
}
impl<'t> Index<usize> for Captures<'t> {
type Output = str;
fn index(&self, i: usize) -> &str {
self.get(i).map(|m| m.as_str())
.unwrap_or_else(|| panic!("no group at index '{}'", i))
}
}
impl<'t, 'i> Index<&'i str> for Captures<'t> {
type Output = str;
fn index<'a>(&'a self, name: &'i str) -> &'a str {
self.name(name).map(|m| m.as_str())
.unwrap_or_else(|| panic!("no group named '{}'", name))
}
}
pub struct SubCaptureMatches<'c, 't: 'c> {
caps: &'c Captures<'t>,
it: SubCapturesPosIter<'c>,
}
impl<'c, 't> Iterator for SubCaptureMatches<'c, 't> {
type Item = Option<Match<'t>>;
fn next(&mut self) -> Option<Option<Match<'t>>> {
self.it.next()
.map(|cap| cap.map(|(s, e)| Match::new(self.caps.text, s, e)))
}
}
pub struct CaptureMatches<'r, 't>(CaptureMatchesInner<'r, 't>);
enum CaptureMatchesInner<'r, 't> {
Dynamic(re_trait::CaptureMatches<'t, ExecNoSyncStr<'r>>),
Plugin(re_trait::CaptureMatches<'t, Plugin>),
}
impl<'r, 't> Iterator for CaptureMatches<'r, 't> {
type Item = Captures<'t>;
fn next(&mut self) -> Option<Captures<'t>> {
match self.0 {
CaptureMatchesInner::Dynamic(ref mut it) => {
let named = it.regex().capture_name_idx().clone();
it.next().map(|locs| Captures {
text: it.text(),
locs: locs,
named_groups: NamedGroups::Dynamic(named),
})
}
CaptureMatchesInner::Plugin(ref mut it) => {
it.next().map(|locs| Captures {
text: it.text(),
locs: locs,
named_groups: NamedGroups::Plugin(it.regex().groups),
})
}
}
}
}
pub struct Matches<'r, 't>(MatchesInner<'r, 't>);
enum MatchesInner<'r, 't> {
Dynamic(re_trait::Matches<'t, ExecNoSyncStr<'r>>),
Plugin(re_trait::Matches<'t, Plugin>),
}
impl<'r, 't> Matches<'r, 't> {
fn text(&self) -> &'t str {
match self.0 {
MatchesInner::Dynamic(ref it) => it.text(),
MatchesInner::Plugin(ref it) => it.text(),
}
}
}
impl<'r, 't> Iterator for Matches<'r, 't> {
type Item = Match<'t>;
fn next(&mut self) -> Option<Match<'t>> {
let text = self.text();
match self.0 {
MatchesInner::Dynamic(ref mut it) => {
it.next().map(|(s, e)| Match::new(text, s, e))
}
MatchesInner::Plugin(ref mut it) => {
it.next().map(|(s, e)| Match::new(text, s, e))
}
}
}
}
pub trait Replacer {
fn replace_append(&mut self, caps: &Captures, dst: &mut String);
fn no_expansion<'r>(&'r mut self) -> Option<Cow<'r, str>> {
None
}
}
impl<'a> Replacer for &'a str {
fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
caps.expand(*self, dst);
}
fn no_expansion(&mut self) -> Option<Cow<str>> {
match memchr(b'$', self.as_bytes()) {
Some(_) => None,
None => Some(Cow::Borrowed(*self)),
}
}
}
impl<F> Replacer for F where F: FnMut(&Captures) -> String {
fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
dst.push_str(&(*self)(caps));
}
}
pub struct NoExpand<'t>(pub &'t str);
impl<'t> Replacer for NoExpand<'t> {
fn replace_append(&mut self, _: &Captures, dst: &mut String) {
dst.push_str(self.0);
}
fn no_expansion(&mut self) -> Option<Cow<str>> {
Some(Cow::Borrowed(self.0))
}
}