#![cfg_attr(any(not(doctest), feature = "nightly"), no_std)]
#![cfg_attr(feature = "nightly", feature(pattern))]
extern crate alloc as std;
use std::{
borrow::{Cow, ToOwned},
string::String,
};
pub trait Pattern<'s> {
type MatchIndices: Iterator<Item = (usize, &'s str)>;
fn match_indices_in(self, s: &'s str) -> Self::MatchIndices;
}
macro_rules! impl_pattern {
($ty:ty $(where $($bound:tt)*)?) => {
impl<'s $(, $($bound)*)?> Pattern<'s> for $ty {
type MatchIndices = std::str::MatchIndices<'s, Self>;
fn match_indices_in(self, s: &'s str) -> Self::MatchIndices {
s.match_indices(self)
}
}
};
}
#[cfg(not(feature = "nightly"))]
const _: () = {
impl_pattern!(char);
impl_pattern!(&str);
impl_pattern!(&String);
impl_pattern!(&[char]);
impl_pattern!(&&str);
impl_pattern!(F where F: FnMut(char) -> bool);
};
#[cfg(feature = "nightly")]
impl_pattern!(P where P: std::str::pattern::Pattern<'s>);
pub trait CowUtils<'s> {
type Output;
fn cow_replace(self, pattern: impl Pattern<'s>, to: &str) -> Self::Output;
fn cow_replacen(self, from: impl Pattern<'s>, to: &str, count: usize) -> Self::Output;
fn cow_to_ascii_lowercase(self) -> Self::Output;
fn cow_to_lowercase(self) -> Self::Output;
fn cow_to_ascii_uppercase(self) -> Self::Output;
fn cow_to_uppercase(self) -> Self::Output;
}
unsafe fn cow_replace<'s>(
src: &'s str,
match_indices: impl Iterator<Item = (usize, &'s str)>,
to: &str,
) -> Cow<'s, str> {
let mut result = Cow::default();
let mut last_start = 0;
for (index, matched) in match_indices {
result += src.get_unchecked(last_start..index);
if !to.is_empty() {
result.to_mut().push_str(to);
}
last_start = index + matched.len();
}
result += src.get_unchecked(last_start..);
result
}
impl<'s> CowUtils<'s> for &'s str {
type Output = Cow<'s, str>;
fn cow_replace(self, pattern: impl Pattern<'s>, to: &str) -> Self::Output {
unsafe { cow_replace(self, pattern.match_indices_in(self), to) }
}
fn cow_replacen(self, pattern: impl Pattern<'s>, to: &str, count: usize) -> Self::Output {
unsafe { cow_replace(self, pattern.match_indices_in(self).take(count), to) }
}
fn cow_to_ascii_lowercase(self) -> Self::Output {
match self.as_bytes().iter().position(u8::is_ascii_uppercase) {
Some(pos) => {
let mut output = self.to_owned();
unsafe { output.get_unchecked_mut(pos..) }.make_ascii_lowercase();
Cow::Owned(output)
}
None => Cow::Borrowed(self),
}
}
fn cow_to_lowercase(self) -> Self::Output {
if self.chars().any(changes_when_lowercased) {
Cow::Owned(self.to_lowercase())
} else {
Cow::Borrowed(self)
}
}
fn cow_to_ascii_uppercase(self) -> Self::Output {
match self.as_bytes().iter().position(u8::is_ascii_lowercase) {
Some(pos) => {
let mut output = self.to_owned();
unsafe { output.get_unchecked_mut(pos..) }.make_ascii_uppercase();
Cow::Owned(output)
}
None => Cow::Borrowed(self),
}
}
fn cow_to_uppercase(self) -> Self::Output {
match self.find(changes_when_uppercased) {
Some(pos) => {
let mut output = String::with_capacity(self.len());
output.push_str(unsafe { self.get_unchecked(..pos) });
output.extend(
unsafe { self.get_unchecked(pos..) }
.chars()
.flat_map(char::to_uppercase),
);
Cow::Owned(output)
}
None => Cow::Borrowed(self),
}
}
}
fn changes_when_lowercased(c: char) -> bool {
!core::iter::once(c).eq(c.to_lowercase())
}
fn changes_when_uppercased(c: char) -> bool {
!core::iter::once(c).eq(c.to_uppercase())
}