use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
use std::iter::FusedIterator;
use std::ops::{Index, Range};
use std::str::FromStr;
use std::sync::Arc;
use crate::find_byte::find_byte;
use crate::error::Error;
use crate::exec::{Exec, ExecNoSyncStr};
use crate::expand::expand_str;
use crate::re_builder::unicode::RegexBuilder;
use crate::re_trait::{self, RegularExpression, SubCapturesPosIter};
pub fn escape(text: &str) -> String {
regex_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 range(&self) -> Range<usize> {
self.start..self.end
}
#[inline]
pub fn as_str(&self) -> &'t str {
&self.text[self.range()]
}
#[inline]
fn new(haystack: &'t str, start: usize, end: usize) -> Match<'t> {
Match { text: haystack, start, end }
}
}
impl<'t> From<Match<'t>> for &'t str {
fn from(m: Match<'t>) -> &'t str {
m.as_str()
}
}
impl<'t> From<Match<'t>> for Range<usize> {
fn from(m: Match<'t>) -> Range<usize> {
m.range()
}
}
#[derive(Clone)]
pub struct Regex(Exec);
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(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> {
Matches(self.0.searcher_str().find_iter(text))
}
pub fn captures<'t>(&self, text: &'t str) -> Option<Captures<'t>> {
let mut locs = self.capture_locations();
self.captures_read_at(&mut locs, text, 0).map(move |_| Captures {
text,
locs: locs.0,
named_groups: self.0.capture_name_idx().clone(),
})
}
pub fn captures_iter<'r, 't>(
&'r self,
text: &'t str,
) -> CaptureMatches<'r, 't> {
CaptureMatches(self.0.searcher_str().captures_iter(text))
}
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)
}
pub fn shortest_match_at(
&self,
text: &str,
start: usize,
) -> Option<usize> {
self.0.searcher_str().shortest_match_at(text, start)
}
pub fn is_match_at(&self, text: &str, start: usize) -> bool {
self.0.searcher_str().is_match_at(text, start)
}
pub fn find_at<'t>(
&self,
text: &'t str,
start: usize,
) -> Option<Match<'t>> {
self.0
.searcher_str()
.find_at(text, start)
.map(|(s, e)| Match::new(text, s, e))
}
pub fn captures_read<'t>(
&self,
locs: &mut CaptureLocations,
text: &'t str,
) -> Option<Match<'t>> {
self.captures_read_at(locs, text, 0)
}
pub fn captures_read_at<'t>(
&self,
locs: &mut CaptureLocations,
text: &'t str,
start: usize,
) -> Option<Match<'t>> {
self.0
.searcher_str()
.captures_read_at(&mut locs.0, text, start)
.map(|(s, e)| Match::new(text, s, e))
}
#[doc(hidden)]
pub fn read_captures_at<'t>(
&self,
locs: &mut CaptureLocations,
text: &'t str,
start: usize,
) -> Option<Match<'t>> {
self.captures_read_at(locs, text, start)
}
}
impl Regex {
pub fn as_str(&self) -> &str {
&self.0.regex_strings()[0]
}
pub fn capture_names(&self) -> CaptureNames<'_> {
CaptureNames(self.0.capture_names().iter())
}
pub fn captures_len(&self) -> usize {
self.0.capture_names().len()
}
pub fn capture_locations(&self) -> CaptureLocations {
CaptureLocations(self.0.searcher_str().locations())
}
#[doc(hidden)]
pub fn locations(&self) -> CaptureLocations {
CaptureLocations(self.0.searcher_str().locations())
}
}
#[derive(Clone, Debug)]
pub struct CaptureNames<'r>(::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>> {
self.0
.next()
.as_ref()
.map(|slot| slot.as_ref().map(|name| name.as_ref()))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn count(self) -> usize {
self.0.count()
}
}
impl<'r> ExactSizeIterator for CaptureNames<'r> {}
impl<'r> FusedIterator for CaptureNames<'r> {}
#[derive(Debug)]
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.0.text();
match self.finder.next() {
None => {
if self.last > text.len() {
None
} else {
let s = &text[self.last..];
self.last = text.len() + 1; Some(s)
}
}
Some(m) => {
let matched = &text[self.last..m.start()];
self.last = m.end();
Some(matched)
}
}
}
}
impl<'r, 't> FusedIterator for Split<'r, 't> {}
#[derive(Debug)]
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 {
return self.splits.next();
}
let text = self.splits.finder.0.text();
if self.splits.last > text.len() {
None
} else {
Some(&text[self.splits.last..])
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.n))
}
}
impl<'r, 't> FusedIterator for SplitN<'r, 't> {}
#[derive(Clone, Debug)]
pub struct CaptureLocations(re_trait::Locations);
#[doc(hidden)]
pub type Locations = CaptureLocations;
impl CaptureLocations {
#[inline]
pub fn get(&self, i: usize) -> Option<(usize, usize)> {
self.0.pos(i)
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[doc(hidden)]
#[inline]
pub fn pos(&self, i: usize) -> Option<(usize, usize)> {
self.get(i)
}
}
pub struct Captures<'t> {
text: &'t str,
locs: re_trait::Locations,
named_groups: Arc<HashMap<String, usize>>,
}
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.get(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 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, &String> =
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))
}
}
#[derive(Clone, Debug)]
pub struct SubCaptureMatches<'c, 't> {
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)))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.it.size_hint()
}
fn count(self) -> usize {
self.it.count()
}
}
impl<'c, 't> ExactSizeIterator for SubCaptureMatches<'c, 't> {}
impl<'c, 't> FusedIterator for SubCaptureMatches<'c, 't> {}
#[derive(Debug)]
pub struct CaptureMatches<'r, 't>(
re_trait::CaptureMatches<'t, ExecNoSyncStr<'r>>,
);
impl<'r, 't> Iterator for CaptureMatches<'r, 't> {
type Item = Captures<'t>;
fn next(&mut self) -> Option<Captures<'t>> {
self.0.next().map(|locs| Captures {
text: self.0.text(),
locs,
named_groups: self.0.regex().capture_name_idx().clone(),
})
}
}
impl<'r, 't> FusedIterator for CaptureMatches<'r, 't> {}
#[derive(Debug)]
pub struct Matches<'r, 't>(re_trait::Matches<'t, ExecNoSyncStr<'r>>);
impl<'r, 't> Iterator for Matches<'r, 't> {
type Item = Match<'t>;
fn next(&mut self) -> Option<Match<'t>> {
let text = self.0.text();
self.0.next().map(|(s, e)| Match::new(text, s, e))
}
}
impl<'r, 't> FusedIterator for Matches<'r, 't> {}
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
}
fn by_ref<'r>(&'r mut self) -> ReplacerRef<'r, Self> {
ReplacerRef(self)
}
}
#[derive(Debug)]
pub struct ReplacerRef<'a, R: ?Sized>(&'a mut R);
impl<'a, R: Replacer + ?Sized + 'a> Replacer for ReplacerRef<'a, R> {
fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
self.0.replace_append(caps, dst)
}
fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
self.0.no_expansion()
}
}
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>> {
no_expansion(self)
}
}
impl<'a> Replacer for &'a String {
fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
self.as_str().replace_append(caps, dst)
}
fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
no_expansion(self)
}
}
impl Replacer for String {
fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
self.as_str().replace_append(caps, dst)
}
fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
no_expansion(self)
}
}
impl<'a> Replacer for Cow<'a, str> {
fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
self.as_ref().replace_append(caps, dst)
}
fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
no_expansion(self)
}
}
impl<'a> Replacer for &'a Cow<'a, str> {
fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
self.as_ref().replace_append(caps, dst)
}
fn no_expansion(&mut self) -> Option<Cow<'_, str>> {
no_expansion(self)
}
}
fn no_expansion<T: AsRef<str>>(t: &T) -> Option<Cow<'_, str>> {
let s = t.as_ref();
match find_byte(b'$', s.as_bytes()) {
Some(_) => None,
None => Some(Cow::Borrowed(s)),
}
}
impl<F, T> Replacer for F
where
F: FnMut(&Captures<'_>) -> T,
T: AsRef<str>,
{
fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
dst.push_str((*self)(caps).as_ref());
}
}
#[derive(Clone, Debug)]
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))
}
}