use crate::{util::merge_ranges, Matcher};
#[cfg(feature = "serde")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::{
fmt::{self, Debug, Display, Formatter},
ops::{Deref, Range},
};
#[derive(Clone, Eq, Hash)]
pub struct CuredString(pub(crate) String);
impl CuredString {
#[inline(always)]
pub fn find<'a, 'b>(&'a self, other: &'b str) -> Matcher<'a, 'b> {
Matcher::new(self, other)
}
pub fn find_multiple<S, O>(&self, other: O) -> Vec<Range<usize>>
where
S: AsRef<str>,
O: IntoIterator<Item = S>,
{
let other = other.into_iter();
let mut ranges = Vec::with_capacity(other.size_hint().0);
for o in other {
ranges.extend(self.find(o.as_ref()));
}
merge_ranges(&mut ranges);
ranges
}
fn censor_inner<I>(&mut self, original: &str, matches: I, with: char)
where
I: IntoIterator<Item = Range<usize>>,
{
let mut with_str = String::new();
let mut char_diff = 0isize;
for mat in matches {
let cap = original[mat.clone()].chars().count() * with.len_utf8();
with_str.reserve_exact(cap);
for _ in (with_str.len()..cap).step_by(with.len_utf8()) {
with_str.push(with);
}
self.0.replace_range(
(mat.start as isize + char_diff) as usize..(mat.end as isize + char_diff) as _,
&with_str[..cap],
);
char_diff += cap as isize - mat.len() as isize;
}
}
pub fn censor(&mut self, other: &str, with: char) {
let original = self.clone();
self.censor_inner(&original, original.find(other), with);
}
pub fn censor_multiple<S, O>(&mut self, other: O, with: char)
where
S: AsRef<str>,
O: IntoIterator<Item = S>,
{
let original = self.clone();
self.censor_inner(&original, original.find_multiple(other), with);
}
fn replace_inner<I>(&mut self, matches: I, with: &str)
where
I: IntoIterator<Item = Range<usize>>,
{
let mut char_diff = 0isize;
for mat in matches {
self.0.replace_range(
(mat.start as isize + char_diff) as usize..(mat.end as isize + char_diff) as _,
with,
);
char_diff += with.len() as isize - mat.len() as isize;
}
}
#[inline(always)]
pub fn replace(&mut self, other: &str, with: &str) {
self.replace_inner(self.clone().find(other), with);
}
#[inline(always)]
pub fn replace_multiple<S, O>(&mut self, other: O, with: &str)
where
S: AsRef<str>,
O: IntoIterator<Item = S>,
{
self.replace_inner(self.clone().find_multiple(other), with);
}
pub fn starts_with(&self, other: &str) -> bool {
let mut iter = self.find(other);
let Some(mat) = iter.next() else {
return false;
};
mat.start == 0
}
pub fn ends_with(&self, other: &str) -> bool {
let Some(last) = self.find(other).last() else {
return false;
};
last.end == self.len()
}
pub fn contains(&self, other: &str) -> bool {
let mut iter = self.find(other);
iter.next().is_some()
}
}
impl From<CuredString> for String {
#[inline(always)]
fn from(val: CuredString) -> Self {
val.0
}
}
impl AsRef<str> for CuredString {
#[inline(always)]
fn as_ref(&self) -> &str {
&self.0
}
}
impl<S> PartialEq<S> for CuredString
where
S: AsRef<str> + ?Sized,
{
#[inline(always)]
fn eq(&self, other: &S) -> bool {
Matcher::is_equal(self, other.as_ref())
}
}
impl Debug for CuredString {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
impl Display for CuredString {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl Deref for CuredString {
type Target = String;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl Serialize for CuredString {
#[inline(always)]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self)
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> Deserialize<'de> for CuredString {
#[inline(always)]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Deserialize::deserialize(deserializer)
.and_then(|s: &str| crate::cure!(s).map_err(de::Error::custom))
}
}