use std::collections::BTreeMap;
pub fn multi_replace(s: &str, pats: &[(&str, &str)]) -> String {
let mut indices = BTreeMap::new();
for (pat, new) in pats {
for (i, p) in s.match_indices(pat) {
if indices
.range(..=i)
.next_back()
.map(|(pos, (len, _))| pos + len <= i)
.unwrap_or(true)
{
indices.insert(i, (p.len(), *new));
}
}
}
let mut result = String::new();
let mut end = 0usize;
for (pos, (len, new)) in indices {
result.push_str(unsafe { s.get_unchecked(end..pos) });
result.push_str(new);
end = pos + len;
}
if end < s.len() {
result.push_str(unsafe { s.get_unchecked(end..) });
}
result
}
pub fn exchange(s: &str, a: &str, b: &str) -> String {
let pat = if a.len() > b.len() {
[(a, b), (b, a)]
} else {
[(b, a), (a, b)]
};
multi_replace(s, &pat)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn replace() {
let s = "Hana is cute";
let r = multi_replace(s, &[("Hana", "Minami"), ("cute", "kawaii")]);
assert_eq!(r, "Minami is kawaii");
}
#[test]
fn not_match() {
assert_eq!(
"Hana is kawaii",
multi_replace("Hana is cute", &[("Rica", "Minami"), ("cute", "kawaii")])
)
}
#[test]
fn remain() {
assert_eq!(
"Hana is kawaii",
multi_replace("Minami is kawaii", &[("Minami", "Hana")])
)
}
#[test]
fn overlap() {
assert_eq!(
"Both Minami and Hana are kawaii",
multi_replace(
"Bouh Aoi and Hana are kawaii",
&[("Bouh", "Both"), ("Aoi", "Minami"), ("oi", "io")]
)
)
}
#[test]
fn exchange() {
let s = "Both Hana and Minami are kawaii";
assert_eq!(
"Both Minami and Hana are kawaii",
super::exchange(s, "Minami", "Hana")
);
assert_eq!(
"Both Minami and Hana are kawaii",
super::exchange(s, "Hana", "Minami")
);
assert_eq!(
"Both Hinata and Hina are kawaii",
super::exchange("Both Hina and Hinata are kawaii", "Hina", "Hinata")
);
assert_eq!(
"Both Hinata and Hina are kawaii",
super::exchange("Both Hina and Hinata are kawaii", "Hinata", "Hina")
);
}
}