#![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 {
ReplaceWriter::new(f, self.needle, &self.replacement)
.write_fmt(format_args!("{}", 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 start = self.needle_pos;
let needle = &self.needle[start..];
if s.len() < needle.len() {
let needle = &needle[..s.len()];
if s.starts_with(needle) {
self.needle_pos += s.len();
self.buffer.push_str(s);
Ok(())
} else {
self.needle_pos = 0;
self.writer.write_str(&self.buffer)?;
self.buffer.clear();
self.writer.write_str(s)
}
} else {
if s.starts_with(needle) {
self.buffer.clear();
self.writer.write_fmt(format_args!("{}", self.replacement))
} else {
self.needle_pos = 0;
self.writer.write_str(&self.buffer)?;
self.buffer.clear();
self.writer.write_str(s)
}
}
}
}
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!(
"oneonetwothreethree",
format_args!(
"{}{}",
"one!HERE!".lazy_replace("!HERE!", "tw"),
"!HERE!three".lazy_replace("!HERE!", "o")
)
.replace_display("two", "one!HERE!three".lazy_replace("!HERE!", "two"))
.to_string()
);
}
}