#![cfg_attr(feature = "nightly", feature(pattern))]
#![warn(missing_docs)]
extern crate memchr;
use std::{
fmt::{self, Write},
ops::Deref,
};
#[cfg(not(feature = "nightly"))]
pub mod pattern;
#[cfg(feature = "nightly")]
pub use std::str::pattern;
use self::pattern::{Pattern, SearchStep, Searcher};
pub struct ReplaceDisplay<'a, H, R> {
haystack: H,
needle: &'a str,
replacement: R,
}
impl<'a, H, R> ReplaceDisplay<'a, H, R> {
pub fn new(haystack: H, needle: &'a str, replacement: R) -> Self {
ReplaceDisplay {
haystack,
needle,
replacement,
}
}
}
impl<'a, D, R> fmt::Display for ReplaceDisplay<'a, D, R>
where
D: fmt::Display,
R: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
ReplaceWriter::new(f, self.needle, &self.replacement),
"{}",
self.haystack
)
}
}
pub struct ReplaceWriter<'a, W, R> {
writer: W,
needle_pos: usize,
needle: &'a str,
replacement: R,
buffer: String,
}
impl<'a, W, R> ReplaceWriter<'a, W, R>
where
W: fmt::Write,
R: fmt::Display,
{
pub fn new(writer: W, needle: &'a str, replacement: R) -> Self {
ReplaceWriter {
writer,
needle_pos: 0,
needle,
replacement,
buffer: String::new(),
}
}
}
impl<'a, W, R> fmt::Write for ReplaceWriter<'a, W, R>
where
W: fmt::Write,
R: fmt::Display,
{
fn write_str(&mut self, s: &str) -> fmt::Result {
let rest_needle = &self.needle[self.needle_pos..];
if s.len() < rest_needle.len() && s.starts_with(&rest_needle[..s.len()]) {
self.needle_pos += s.len();
self.buffer.push_str(s);
} else {
self.needle_pos = 0;
if s == rest_needle {
self.buffer.clear();
write!(self.writer, "{}", self.replacement)?;
} else {
self.writer.write_str(&self.buffer)?;
self.buffer.clear();
if let Some(first_char) = self.needle.chars().next() {
let mut s = s;
while let Some(i) = s.find(first_char) {
self.writer.write_str(&s[..i])?;
s = &s[i..];
dbg!(s);
let mut len = first_char.len_utf8();
let needle_bytes = self.needle.as_bytes();
let s_bytes = s.as_bytes();
while needle_bytes
.get(len)
.and_then(|needle| s_bytes.get(len).map(|haystack| haystack == needle))
.unwrap_or(false)
{
len += 1;
}
if len == self.needle.len() {
write!(self.writer, "{}", self.replacement)?;
s = &s[len..];
} else if len == s.len() {
self.buffer.push_str(&s[i..]);
self.needle_pos = len;
return Ok(());
} else {
self.writer.write_str(&s[..len])?;
s = &s[len..];
}
}
dbg!(s);
self.writer.write_str(s)?;
}
}
}
Ok(())
}
}
pub struct ReplacedString<'a, P, R> {
haystack: &'a str,
needle: P,
replacement: R,
}
impl<'a, P, R> ReplacedString<'a, P, R> {
pub fn new(haystack: &'a str, needle: P, replacement: R) -> Self {
ReplacedString {
haystack,
needle,
replacement,
}
}
}
pub trait LazyReplace {
fn lazy_replace<P, R>(&self, pat: P, replacement: R) -> ReplacedString<'_, P, R>;
}
impl<T> LazyReplace for T
where
T: Deref<Target = str>,
{
fn lazy_replace<P, R>(&self, needle: P, replacement: R) -> ReplacedString<'_, P, R> {
ReplacedString {
needle,
replacement,
haystack: &*self,
}
}
}
impl LazyReplace for str {
fn lazy_replace<P, R>(&self, needle: P, replacement: R) -> ReplacedString<'_, P, R> {
ReplacedString {
needle,
replacement,
haystack: &*self,
}
}
}
pub trait LazyReplaceDisplay: Sized {
fn replace_display<'a, R>(self, pat: &'a str, replacement: R) -> ReplaceDisplay<'a, Self, R>;
}
impl<T> LazyReplaceDisplay for T
where
T: fmt::Display,
{
fn replace_display<'a, R>(self, pat: &'a str, replacement: R) -> ReplaceDisplay<'a, Self, R> {
ReplaceDisplay::new(self, pat, replacement)
}
}
impl<'a, P, R> fmt::Display for ReplacedString<'a, P, R>
where
P: Pattern<'a> + Clone,
R: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut searcher = self.needle.clone().into_searcher(self.haystack);
loop {
match searcher.next() {
SearchStep::Match(_, _) => write!(f, "{}", self.replacement)?,
SearchStep::Reject(start, end) => write!(f, "{}", &self.haystack[start..end])?,
SearchStep::Done => break,
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{LazyReplace, LazyReplaceDisplay};
#[test]
fn replace_string() {
assert_eq!(
"onetwothree",
"one!HERE!three".lazy_replace("!HERE!", "two").to_string()
);
assert_eq!(
"onetwothree",
"onetwo!HERE!".lazy_replace("!HERE!", "three").to_string()
);
assert_eq!(
"onetwothreethree",
"onetwo!HERE!!HERE!"
.lazy_replace("!HERE!", "three")
.to_string()
);
}
#[test]
fn replace_display() {
assert_eq!(
"foobar",
"foo!HERE!".replace_display("!HERE!", "bar").to_string()
);
assert_eq!(
"foobar",
"!HERE!bar".replace_display("!HERE!", "foo").to_string()
);
assert_eq!(
"foobarbaz",
format!(
"{}{}",
"foo!HERE!".replace_display("!HERE!", "ba"),
"!HERE!baz".replace_display("!HERE!", "r")
)
);
assert_eq!(
"fooonetwothreebaz",
format_args!(
"{}{}",
"foo!HERE!".replace_display("!HERE!", "ba"),
"!HERE!baz".replace_display("!HERE!", "r")
)
.replace_display("bar", "one!HERE!three".replace_display("!HERE!", "two"))
.to_string()
);
}
}