use std::borrow::{Borrow, BorrowMut};
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::num::NonZeroUsize;
use std::ops;
use std::str::FromStr;
use code_product::product;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize, Serializer};
#[allow(clippy::wildcard_imports)]
use crate::*;
#[derive(Debug)]
pub enum Error {
Capacity,
Range,
NoGuardOnStatic,
GuardAlreadyExists,
TryReserveError,
DifferentOrigin,
}
#[repr(transparent)]
pub struct CowStr(pub(crate) CowStrInner);
#[derive(Debug)]
pub(crate) enum CowStrInner {
Static(&'static str),
Shared(RcString),
}
impl CowStrInner {
#[inline]
const fn new_shared(ptr: RcString) -> CowStrInner {
CowStrInner::Shared(ptr)
}
}
impl CowStr {
#[must_use]
#[inline]
pub const fn new() -> Self {
CowStr(CowStrInner::Static(""))
}
#[must_use]
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
CowStr(CowStrInner::new_shared(RcString::allocate(capacity)))
}
#[must_use]
#[inline]
pub const fn from_static(s: &'static str) -> Self {
CowStr(CowStrInner::Static(s))
}
#[must_use]
#[inline]
pub fn format(args: fmt::Arguments<'_>) -> CowStr {
if let Some(static_str) = args.as_str() {
CowStr::from_static(static_str)
} else {
use std::fmt::Write;
#[cfg(feature = "nightly_fmt_internals")]
let mut output = CowStr::with_capacity(args.estimated_capacity());
#[cfg(not(feature = "nightly_fmt_internals"))]
let mut output = CowStr::new();
output
.write_fmt(args)
.expect("a formatting trait implementation returned an error");
output
}
}
#[must_use]
#[inline]
pub fn replicate(&self) -> Self {
match &self.0 {
CowStrInner::Static(s) => CowStr(CowStrInner::Static(s)),
CowStrInner::Shared(_) => CowStr::from(self.as_str()),
}
}
pub fn set_static(&mut self, s: &'static str) {
if let CowStrInner::Shared(r) = self.0 {
if r.decrement_strong_count() {
unsafe { RcString::dealloc(r) };
}
}
self.0 = CowStrInner::Static(s);
}
#[inline]
fn make_mut(&mut self, reserve: usize) -> &mut RcString {
unsafe {
self.make_mut_intern(reserve, false).unwrap_unchecked()
}
}
#[allow(clippy::wrong_self_convention)]
fn make_mut_intern(
&mut self,
reserve: usize,
tryreserve: bool,
) -> Result<&mut RcString, Error> {
match self.0 {
CowStrInner::Shared(r) if r.strong_count() > 1 => {
self.0 = CowStrInner::new_shared(RcString::from_str(r.as_str(), reserve));
if r.decrement_strong_count() {
unsafe { RcString::dealloc(r) };
}
}
CowStrInner::Shared(r) if r.spare_capacity() < reserve => {
self.0 = CowStrInner::new_shared(unsafe {
RcString::grow(r, reserve, tryreserve)?
});
}
CowStrInner::Static(s) => {
self.0 = CowStrInner::new_shared(RcString::from_str(s, reserve));
}
CowStrInner::Shared(_) => { }
}
let CowStrInner::Shared(ref mut rc) = self.0 else {
unsafe { std::hint::unreachable_unchecked() }
};
debug_assert_eq!(rc.strong_count(), 1);
Ok(rc)
}
#[inline]
pub(crate) const fn rcstring(&self) -> Option<&RcString> {
if let CowStrInner::Shared(ref rc) = self.0 {
Some(rc)
} else {
None
}
}
#[inline]
pub fn push(&mut self, c: char) {
unsafe {
self.make_mut(c.len_utf8()).push(c);
}
}
#[inline]
#[doc(hidden)]
#[deprecated = "use push()"]
#[mutants::skip]
pub fn push_char(&mut self, c: char) {
unsafe {
self.make_mut(c.len_utf8()).push(c);
}
}
#[inline]
pub fn push_str(&mut self, s: &str) {
unsafe {
self.make_mut(s.len()).push_str(s);
}
}
#[inline]
pub fn guard(&self) -> Result<CowStrPushGuard, Error> {
CowStrPushGuard::from(self)
}
#[allow(clippy::missing_errors_doc)]
#[inline]
pub fn deref_static(&self) -> Result<&'static str, &str> {
match &self.0 {
CowStrInner::Static(s) => Ok(s),
CowStrInner::Shared(s) => Err(s.as_str()),
}
}
#[must_use]
#[inline]
#[allow(clippy::missing_panics_doc)]
pub fn strong_count(&self) -> Option<NonZeroUsize> {
match self.0 {
CowStrInner::Static(_) => None,
CowStrInner::Shared(r) => Some(NonZeroUsize::new(r.strong_count()).unwrap()),
}
}
#[must_use]
#[inline]
pub fn as_str(&self) -> &str {
match self.deref_static() {
Ok(s) | Err(s) => s,
}
}
#[inline]
pub fn as_mut_str(&mut self) -> &mut str {
unsafe {
self.make_mut(0).as_mut_str()
}
}
#[must_use]
#[inline]
pub fn capacity(&self) -> usize {
match self.0 {
CowStrInner::Static(s) => s.len(),
CowStrInner::Shared(r) => r.capacity(),
}
}
#[must_use]
#[inline]
pub fn spare_capacity(&self) -> usize {
match self.0 {
CowStrInner::Static(_) => 0,
CowStrInner::Shared(r) => r.spare_capacity(),
}
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.make_mut(additional);
}
#[inline]
#[mutants::skip]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), Error> {
self.make_mut_intern(additional, true)?;
Ok(())
}
pub fn shrink_to(&mut self, new_capacity: usize) {
match self.0 {
CowStrInner::Shared(r) if r.strong_count() == 1 => {
self.0 = CowStrInner::new_shared(unsafe {
RcString::shrink(r, new_capacity)
});
}
_ => { }
}
}
#[inline]
#[mutants::skip]
pub fn shrink_to_fit(&mut self) {
self.shrink_to(0);
}
#[inline]
#[must_use]
pub fn shrink_clone(&mut self) -> Self {
self.shrink_to(0);
self.clone()
}
#[inline]
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
self.as_str().as_bytes()
}
#[inline]
#[must_use]
pub fn is_same_as(&self, other: &Self) -> bool {
std::ptr::eq(self.as_str(), other.as_str())
}
#[inline]
#[must_use]
pub fn range_of(&self, substr: &str) -> std::ops::Range<usize> {
assert!(
self.as_bytes().as_ptr_range().contains(&substr.as_ptr()),
"`substr` is not part of `self`"
);
let start = (substr.as_ptr() as usize) - (self.as_ptr() as usize);
start..start + substr.len()
}
#[must_use]
#[inline]
pub fn len(&self) -> usize {
match self.0 {
CowStrInner::Static(s) => s.len(),
CowStrInner::Shared(r) => r.len_relaxed(),
}
}
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
0 == match self.0 {
CowStrInner::Static(s) => s.len(),
CowStrInner::Shared(r) => r.len_relaxed(),
}
}
#[inline]
pub fn pop(&mut self) -> Option<char> {
unsafe {
self.make_mut(0).pop()
}
}
#[inline]
pub fn clear(&mut self) {
unsafe {
self.make_mut(0).clear();
}
}
#[inline]
pub fn truncate(&mut self, new_len: usize) {
if new_len <= self.len() {
assert!(self.as_str().is_char_boundary(new_len));
unsafe {
self.make_mut(0).truncate(new_len);
}
}
}
#[inline]
pub fn remove(&mut self, idx: usize) -> char {
unsafe {
self.make_mut(0).remove(idx)
}
}
pub fn replace_range<R>(&mut self, range: R, replace_with: &str)
where
R: std::slice::SliceIndex<str, Output = str> + Clone,
Self: ops::Index<R, Output = str>,
{
let range_len = self[range.clone()].len();
let reserve_bytes = if replace_with.len() > range_len {
replace_with.len() - range_len
} else {
0
};
unsafe {
self.make_mut(reserve_bytes)
.replace_range(range, replace_with);
}
}
#[inline]
pub fn insert_str(&mut self, idx: usize, string: &str) {
self.replace_range(idx..idx, string);
}
#[inline]
pub fn insert(&mut self, idx: usize, ch: char) {
let mut buf = [0u8; 4];
self.replace_range(idx..idx, ch.encode_utf8(&mut buf));
}
#[must_use]
pub fn leak(mut self) -> &'static mut str {
let ret = unsafe { &mut *(self.make_mut(0).as_mut_str() as *mut str) };
std::mem::forget(self);
ret
}
pub fn read_chars<I: Iterator<Item = char>>(
&mut self,
iter: &mut I,
until: Option<char>,
ignore: Option<char>,
limit: usize,
) -> usize {
let mut count = 0;
iter.take_while(|i| {
({
count += 1;
count < limit
}) && Some(*i) != until
})
.for_each(move |c| {
if Some(c) != ignore {
self.push(c);
}
});
count
}
#[inline]
pub fn from_chars<I: Iterator<Item = char>>(
iter: &mut I,
until: Option<char>,
ignore: Option<char>,
limit: usize,
) -> (Self, usize) {
let mut s = CowStr::new();
let count = s.read_chars(iter, until, ignore, limit);
(s, count)
}
#[inline]
pub fn read_line<I: Iterator<Item = char>>(&mut self, iter: &mut I, limit: usize) -> usize {
self.read_chars(iter, Some('\n'), Some('\r'), limit)
}
#[inline]
pub fn from_line<I: Iterator<Item = char>>(iter: &mut I, limit: usize) -> (Self, usize) {
let mut s = CowStr::new();
let count = s.read_line(iter, limit);
(s, count)
}
pub fn try_read_chars<E, I: Iterator<Item = Result<char, E>>>(
&mut self,
iter: &mut I,
until: Option<char>,
ignore: Option<char>,
limit: usize,
) -> Result<usize, (E, usize)> {
let mut count = 0;
for i in iter {
count += 1;
if count >= limit {
break;
}
match i {
Ok(c) if Some(c) == until => break,
Ok(c) if Some(c) == ignore => { }
Ok(c) => self.push(c),
Err(e) => return Err((e, count)),
}
}
Ok(count)
}
#[inline]
pub fn try_from_chars<E, I: Iterator<Item = Result<char, E>>>(
iter: &mut I,
until: Option<char>,
ignore: Option<char>,
limit: usize,
) -> Result<(Self, usize), (Self, usize, E)> {
let mut s = CowStr::new();
match s.try_read_chars(iter, until, ignore, limit) {
Ok(c) => Ok((s, c)),
Err((e, c)) => Err((s, c, e)),
}
}
#[inline]
pub fn try_read_line<E, I: Iterator<Item = Result<char, E>>>(
&mut self,
iter: &mut I,
limit: usize,
) -> Result<usize, (E, usize)> {
self.try_read_chars(iter, Some('\n'), Some('\r'), limit)
}
#[inline]
pub fn try_from_line<E, I: Iterator<Item = Result<char, E>>>(
iter: &mut I,
limit: usize,
) -> Result<(Self, usize), (Self, usize, E)> {
let mut s = CowStr::new();
match s.try_read_chars(iter, Some('\n'), Some('\r'), limit) {
Ok(c) => Ok((s, c)),
Err((e, c)) => Err((s, c, e)),
}
}
}
#[test]
fn read_chars() {
let test_str = "the quick\nbrown\0🦊";
let mut chars = test_str.chars();
let s = CowStr::from_chars(&mut chars, None, None, 1000).0;
assert_eq!(s, "the quick\nbrown\0🦊");
let mut chars = test_str.chars();
let s = CowStr::from_line(&mut chars, 1000).0;
assert_eq!(s, "the quick");
let s = CowStr::from_chars(&mut chars, Some('\0'), None, 1000).0;
assert_eq!(s, "brown");
let s = CowStr::from_chars(&mut chars, Some('\0'), None, 1000).0;
assert_eq!(s, "🦊");
}
#[test]
fn try_read_chars() {
let test_str = "the quick\nbrown\0🦊";
let mut chars = test_str.chars().map(|i| -> Result<char, ()> { Ok(i) });
let (s, c) = CowStr::try_from_chars(&mut chars, None, None, 1000).unwrap();
assert_eq!(s, "the quick\nbrown\0🦊");
assert_eq!(c, 17);
let mut chars = test_str.chars().map(|i| -> Result<char, &str> { Ok(i) });
let (s, c) = CowStr::try_from_line(&mut chars, 1000).unwrap();
assert_eq!(s, "the quick");
assert_eq!(c, 10);
let (s, c) = CowStr::try_from_chars(&mut chars, Some('\0'), None, 1000).unwrap();
assert_eq!(s, "brown");
assert_eq!(c, 6);
let (s, c) = CowStr::try_from_chars(&mut chars, Some('\0'), None, 1000).unwrap();
assert_eq!(s, "🦊");
assert_eq!(c, 1);
let (s, c) = CowStr::try_from_chars(&mut chars, Some('\0'), None, 1000).unwrap();
assert_eq!(s, "");
assert_eq!(c, 0);
}
#[test]
fn replace_range() {
let mut s = CowStr::from("foobar");
s.replace_range(0..3, "baz");
assert_eq!(s, "bazbar");
let mut s = CowStr::from("foobar");
s.replace_range(3..3, "baz");
assert_eq!(s, "foobazbar");
let mut s = CowStr::from("foobar");
s.replace_range(0..0, "baz");
assert_eq!(s, "bazfoobar");
}
impl Clone for CowStr {
fn clone(&self) -> Self {
match self.0 {
CowStrInner::Static(s) => CowStr(CowStrInner::Static(s)),
CowStrInner::Shared(rc) => {
rc.increment_strong_count();
CowStr(CowStrInner::Shared(rc))
}
}
}
}
impl Drop for CowStr {
fn drop(&mut self) {
match self.0 {
CowStrInner::Shared(rc) => {
if rc.decrement_strong_count() {
unsafe {
RcString::dealloc(rc);
}
}
}
CowStrInner::Static(_) => { }
}
}
}
impl Default for CowStr {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl From<String> for CowStr {
#[inline]
fn from(source: String) -> Self {
CowStr(CowStrInner::new_shared(RcString::from_str(&source, 0)))
}
}
product! {
impl From<$((&String)(&str)(&mut str))> for CowStr {
#[inline]
fn from(source: $0) -> Self {
CowStr(CowStrInner::new_shared(RcString::from_str(&source, 0)))
}
}
}
impl From<char> for CowStr {
#[inline]
fn from(source: char) -> Self {
let mut cowstr = CowStr::new();
cowstr.push(source);
cowstr
}
}
impl From<SubStr> for CowStr {
#[inline]
fn from(source: SubStr) -> Self {
CowStr::from(&*source)
}
}
#[allow(clippy::useless_conversion)]
#[test]
fn from() {
assert_eq!(CowStr::from(String::from("foobar").as_mut_str()), "foobar");
assert_eq!(CowStr::from(String::from("foobar")), "foobar");
assert_eq!(CowStr::from(&String::from("foobar")), "foobar");
assert_eq!(CowStr::from('x'), "x");
let cowstr = CowStr::from("cowstr");
assert_eq!(CowStr::from(cowstr), "cowstr");
assert_eq!(CowStr::from(SubStr::from("cowstr")), "cowstr");
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for CowStr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
CowStrInner::deserialize(deserializer).map(Self)
}
}
#[cfg(feature = "serde")]
impl Serialize for CowStr {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl Serialize for CowStrInner {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(match self {
Self::Static(value) => value,
Self::Shared(shared_value) => shared_value.as_str(),
})
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for CowStrInner {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
String::deserialize(deserializer)
.map(|value| CowStrInner::Shared(RcString::from_str(value.as_str(), 0)))
}
}
#[cfg(feature = "serde")]
#[test]
fn test_ser_se() {
use serde_test::{assert_ser_tokens, Token};
let string = CowStr::from_static("Test String");
assert_ser_tokens(&string, &[Token::String("Test String")])
}
#[cfg(feature = "serde")]
#[test]
fn test_ser_de() {
use serde_test::{assert_tokens, Token};
let string = CowStr::from_static("Test String");
assert_tokens(&string, &[Token::String("Test String")])
}
impl Extend<char> for CowStr {
fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
let iterator = iter.into_iter();
let (lower_bound, _) = iterator.size_hint();
self.reserve(lower_bound);
iterator.for_each(move |c| self.push(c));
}
#[cfg(feature = "nightly_extend_one")]
#[inline]
fn extend_one(&mut self, c: char) {
self.push(c);
}
#[cfg(feature = "nightly_extend_one")]
#[mutants::skip]
#[inline]
fn extend_reserve(&mut self, additional: usize) {
self.reserve(additional);
}
}
impl<'a> Extend<&'a char> for CowStr {
fn extend<I: IntoIterator<Item = &'a char>>(&mut self, iter: I) {
let iterator = iter.into_iter();
let (lower_bound, _) = iterator.size_hint();
self.reserve(lower_bound);
iterator.for_each(move |c| self.push(*c));
}
#[cfg(feature = "nightly_extend_one")]
#[inline]
fn extend_one(&mut self, c: &char) {
self.push(*c);
}
#[cfg(feature = "nightly_extend_one")]
#[mutants::skip]
#[inline]
fn extend_reserve(&mut self, additional: usize) {
self.reserve(additional);
}
}
impl<'a> Extend<&'a str> for CowStr {
fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
let iterator = iter.into_iter();
iterator.for_each(move |s| self.push_str(s));
}
#[cfg(feature = "nightly_extend_one")]
#[inline]
fn extend_one(&mut self, s: &str) {
self.push_str(s);
}
#[cfg(feature = "nightly_extend_one")]
#[mutants::skip]
#[inline]
fn extend_reserve(&mut self, additional: usize) {
self.reserve(additional);
}
}
impl Extend<CowStr> for CowStr {
fn extend<I: IntoIterator<Item = CowStr>>(&mut self, iter: I) {
let iterator = iter.into_iter();
iterator.for_each(move |cowstr| self.push_str(cowstr.as_str()));
}
#[cfg(feature = "nightly_extend_one")]
#[inline]
fn extend_one(&mut self, cowstr: CowStr) {
self.push_str(cowstr.as_str());
}
#[cfg(feature = "nightly_extend_one")]
#[mutants::skip]
#[inline]
fn extend_reserve(&mut self, additional: usize) {
self.reserve(additional);
}
}
impl Extend<SubStr> for CowStr {
fn extend<I: IntoIterator<Item = SubStr>>(&mut self, iter: I) {
let iterator = iter.into_iter();
iterator.for_each(move |substr| self.push_str(&substr));
}
#[cfg(feature = "nightly_extend_one")]
#[inline]
fn extend_one(&mut self, substr: SubStr) {
self.push_str(&substr);
}
#[cfg(feature = "nightly_extend_one")]
#[mutants::skip]
#[inline]
fn extend_reserve(&mut self, additional: usize) {
self.reserve(additional);
}
}
#[allow(clippy::useless_conversion)]
#[test]
fn extend() {
let mut cowstr = CowStr::new();
cowstr.extend("foobar".chars());
assert_eq!(cowstr, "foobar");
let foobar = ['f', 'o', 'o', 'b', 'a', 'r'];
cowstr.extend(foobar.iter());
assert_eq!(cowstr, "foobarfoobar");
let strs = ["foo", "bar", "baz"];
cowstr.extend(strs.into_iter());
assert_eq!(cowstr, "foobarfoobarfoobarbaz");
let cowstrs = [
CowStr::from("foo"),
CowStr::from("bar"),
CowStr::from("baz"),
];
cowstr.extend(cowstrs.into_iter());
assert_eq!(cowstr, "foobarfoobarfoobarbazfoobarbaz");
let substrs = [
SubStr::from("foo"),
SubStr::from("bar"),
SubStr::from("barf"),
];
cowstr.extend(substrs.into_iter());
assert_eq!(cowstr, "foobarfoobarfoobarbazfoobarbazfoobarbarf");
}
#[cfg(feature = "nightly_extend_one")]
#[test]
fn extend_one() {
let mut cowstr = CowStr::new();
cowstr.extend_one("");
assert_eq!(cowstr, "");
cowstr.extend_one('a');
assert_eq!(cowstr, "a");
cowstr.extend_one(&'b');
assert_eq!(cowstr, "ab");
cowstr.extend_one("ar");
assert_eq!(cowstr, "abar");
cowstr.extend_one(CowStr::from("barf"));
assert_eq!(cowstr, "abarbarf");
cowstr.extend_one(SubStr::from("gaga"));
assert_eq!(cowstr, "abarbarfgaga");
}
product! {
impl<'a> FromIterator<&'a $((char)(str))> for CowStr {
fn from_iter<I: IntoIterator<Item = &'a $0>>(iter: I) -> Self {
let mut buf = CowStr::new();
buf.extend(iter);
buf
}
}
}
product! {
impl FromIterator<$((CowStr)(SubStr))> for CowStr {
fn from_iter<I: IntoIterator<Item = $0>>(iter: I) -> Self {
let mut buf = CowStr::new();
buf.extend(iter);
buf
}
}
}
#[allow(clippy::from_iter_instead_of_collect)]
#[allow(clippy::useless_conversion)]
#[test]
fn from_iterator() {
let foobar = ['f', 'o', 'o', 'b', 'a', 'r'];
let cowstr = CowStr::from_iter(foobar.iter());
assert_eq!(cowstr, "foobar");
let foobar = ["foo", "bar", "baz"];
let cowstr = CowStr::from_iter(foobar.into_iter());
assert_eq!(cowstr, "foobarbaz");
let cowstrs = [
CowStr::from("foo"),
CowStr::from("bar"),
CowStr::from("baz"),
];
let cowstr = CowStr::from_iter(cowstrs.into_iter());
assert_eq!(cowstr, "foobarbaz");
let substrs = [
SubStr::from("foo"),
SubStr::from("bar"),
SubStr::from("barf"),
];
let cowstr = CowStr::from_iter(substrs.into_iter());
assert_eq!(cowstr, "foobarbarf");
}
impl FromStr for CowStr {
type Err = core::convert::Infallible;
#[inline]
fn from_str(s: &str) -> Result<CowStr, Self::Err> {
Ok(CowStr::from(s))
}
}
impl ops::Index<ops::RangeFull> for CowStr {
type Output = str;
#[inline]
fn index(&self, _index: ops::RangeFull) -> &str {
self.as_str()
}
}
product! {
$(R:
(ops::Range<usize>)
(ops::RangeFrom<usize>)
(ops::RangeTo<usize>)
(ops::RangeInclusive<usize>)
(ops::RangeToInclusive<usize>)
)
impl ops::Index<$R> for CowStr {
type Output = str;
#[inline]
fn index(&self, index: $R) -> &str {
&self.as_str()[index]
}
}
}
#[test]
fn index() {
let cowstr = CowStr::from("fööbär");
assert_eq!(&cowstr[..], "fööbär");
assert_eq!(&cowstr[5..8], "bä");
assert_eq!(&cowstr[5..], "bär");
assert_eq!(&cowstr[..6], "fööb");
assert_eq!(&cowstr[5..=8], "bär");
assert_eq!(&cowstr[..=4], "föö");
}
impl ops::IndexMut<ops::RangeFull> for CowStr {
#[inline]
fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str {
self.as_mut_str()
}
}
product! {
$(R:
(ops::Range<usize>)
(ops::RangeFrom<usize>)
(ops::RangeTo<usize>)
(ops::RangeInclusive<usize>)
(ops::RangeToInclusive<usize>)
)
impl ops::IndexMut<$R> for CowStr {
#[inline]
fn index_mut(&mut self, index: $R) -> &mut str {
&mut self.as_mut_str()[index]
}
}
}
#[test]
fn index_mut() {
let mut cowstr = CowStr::from("FOOBAR");
cowstr[..].make_ascii_lowercase();
assert_eq!(cowstr, "foobar");
cowstr[1..3].make_ascii_uppercase();
assert_eq!(cowstr, "fOObar");
cowstr[4..].make_ascii_uppercase();
assert_eq!(cowstr, "fOObAR");
cowstr[..4].make_ascii_lowercase();
assert_eq!(cowstr, "foobAR");
cowstr[..=2].make_ascii_uppercase();
assert_eq!(cowstr, "FOObAR");
cowstr[1..=4].make_ascii_lowercase();
assert_eq!(cowstr, "FoobaR");
}
impl ops::Deref for CowStr {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl ops::DerefMut for CowStr {
#[inline]
fn deref_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
#[test]
fn deref_mut() {
let mut cowstr = CowStr::from("FOOBAR");
cowstr.make_ascii_lowercase();
assert_eq!(cowstr, "foobar");
}
impl AsRef<str> for CowStr {
#[inline]
fn as_ref(&self) -> &str {
self
}
}
impl AsRef<[u8]> for CowStr {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsMut<str> for CowStr {
#[inline]
fn as_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
#[test]
fn as_mut() {
let mut my_string = CowStr::from("as_mut");
my_string.as_mut().make_ascii_uppercase();
assert_eq!(my_string, "AS_MUT");
}
impl Borrow<str> for CowStr {
#[inline]
fn borrow(&self) -> &str {
self
}
}
impl BorrowMut<str> for CowStr {
#[inline]
fn borrow_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
#[test]
fn borrow_mut() {
let mut my_string = CowStr::from("borrow_mut");
<cowstr::CowStr as BorrowMut<str>>::borrow_mut(&mut my_string).make_ascii_uppercase();
assert_eq!(my_string, "BORROW_MUT");
}
impl Eq for CowStr {}
impl PartialEq for CowStr {
#[inline]
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl PartialEq<CowStr> for &CowStr {
#[inline]
fn eq(&self, other: &CowStr) -> bool {
**self == **other
}
}
product! {
impl PartialEq<$((&CowStr)(&str))> for CowStr {
#[inline]
fn eq(&self, other: &$0) -> bool {
**self == **other
}
}
}
impl PartialEq<str> for CowStr {
#[inline]
fn eq(&self, other: &str) -> bool {
**self == *other
}
}
impl PartialEq<CowStr> for &str {
#[inline]
fn eq(&self, other: &CowStr) -> bool {
**self == **other
}
}
impl PartialEq<CowStr> for str {
#[inline]
fn eq(&self, other: &CowStr) -> bool {
*self == **other
}
}
#[cfg(test)]
macro_rules! assert_partial_eq {
($a:expr, $b:expr, $c:expr) => {
assert_eq!($a, $a);
assert_eq!($b, $b);
assert_eq!($a, $b);
assert_eq!($b, $a);
assert_ne!($a, $c);
assert_ne!($c, $a);
assert_ne!($b, $c);
assert_ne!($c, $b);
};
}
#[allow(clippy::disallowed_names)]
#[test]
fn partial_eq() {
let foo = CowStr::from("foo");
let foo2 = CowStr::from("foo");
let bar = CowStr::from("bar");
assert_partial_eq!(foo, foo2, bar);
assert_partial_eq!(foo, "foo", "bar");
assert_partial_eq!(&foo, foo2, bar);
assert_partial_eq!(&foo, "foo", "bar");
}
impl Ord for CowStr {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(**self).cmp(other)
}
}
impl PartialOrd for CowStr {
#[mutants::skip]
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialOrd<&str> for CowStr {
#[mutants::skip]
#[inline]
fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(other)
}
}
impl PartialOrd<CowStr> for &str {
#[mutants::skip]
#[inline]
fn partial_cmp(&self, other: &CowStr) -> Option<std::cmp::Ordering> {
self.partial_cmp(&other.as_ref())
}
}
#[cfg(test)]
macro_rules! assert_partial_ord {
($a:expr, $b:expr, $c:expr) => {
assert!($a < $b);
assert!($b > $a);
assert!($a < $c);
assert!($c > $a);
};
}
#[test]
fn partial_ord() {
let a = CowStr::from("a");
let b = CowStr::from("b");
let c = CowStr::from("c");
assert_partial_ord!(a, b, c);
assert_partial_ord!("a", b, "c");
}
impl ops::Add<char> for CowStr {
type Output = CowStr;
#[inline]
fn add(mut self, other: char) -> CowStr {
self.push(other);
self
}
}
product! {
impl ops::Add<$((CowStr)(SubStr)(&CowStr)(&SubStr)(&str))> for CowStr {
type Output = CowStr;
#[inline]
fn add(mut self, other: $0) -> CowStr {
self.push_str(&other);
self
}
}
}
#[test]
fn add() {
let a = CowStr::from("f");
assert_eq!(a + 'o', "fo");
let a = CowStr::from("b");
assert_eq!(a + "ar", "bar");
let a = CowStr::from("a");
let b = CowStr::from("b");
assert_eq!(a + b, "ab");
let a = CowStr::from("a");
let b = CowStr::from("b");
assert_eq!(a + &b, "ab");
let a = CowStr::from("a");
let b = SubStr::from("b");
assert_eq!(a + b, "ab");
let a = CowStr::from("a");
let b = SubStr::from("b");
assert_eq!(a + &b, "ab");
}
impl ops::AddAssign<char> for CowStr {
#[mutants::skip]
#[inline]
fn add_assign(&mut self, other: char) {
self.push(other);
}
}
product! {
impl ops::AddAssign<$((CowStr)(SubStr)(&CowStr)(&SubStr)(&str))> for CowStr {
#[inline]
fn add_assign(&mut self, other: $0) {
self.push_str(&other);
}
}
}
#[test]
fn add_assign() {
let mut a = CowStr::from("f");
a += CowStr::new();
assert_eq!(a, "f");
a += "oo";
assert_eq!(a, "foo");
a += CowStr::from("b");
assert_eq!(a, "foob");
a += "ar";
assert_eq!(a, "foobar");
a += SubStr::from("baz");
assert_eq!(a, "foobarbaz");
a += &CowStr::from("qux");
assert_eq!(a, "foobarbazqux");
a += &SubStr::from("qax");
assert_eq!(a, "foobarbazquxqax");
}
impl Hash for CowStr {
#[inline]
#[mutants::skip]
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl fmt::Display for CowStr {
#[inline]
#[mutants::skip]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
#[derive(Default, Debug)]
struct NotSend(std::marker::PhantomData<*const ()>);
impl fmt::Write for CowStr {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.push_str(s);
Ok(())
}
#[inline]
fn write_char(&mut self, c: char) -> fmt::Result {
self.push(c);
Ok(())
}
}
#[macro_export]
macro_rules! format {
($($arg:tt)*) => {{
let res = $crate::CowStr::format(std::format_args!($($arg)*));
res
}}
}
pub trait ToCowStr {
fn to_cowstr(&self) -> CowStr;
}
impl<T: fmt::Display + ?Sized> ToCowStr for T {
#[inline]
fn to_cowstr(&self) -> CowStr {
let mut buf = CowStr::new();
fmt::write(&mut buf, format_args!("{self}")).unwrap();
buf
}
}
#[mutants::skip]
impl fmt::Debug for CowStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
let count;
let rcstring;
f.debug_struct("CowStr")
.field("<address>", &self.as_str().as_ptr())
.field(
"<strong_count>",
if let Some(c) = self.strong_count() {
count = c.get().to_string();
&count
} else {
&"static"
},
)
.field(
"<has_guard>",
if let Some(rc) = self.rcstring() {
rcstring = rc.has_guard();
&rcstring
} else {
&false
},
)
.field("capacity", &self.capacity())
.field("len", &self.len())
.field("string", &self.as_str())
.finish()
}
}
#[test]
#[ignore]
fn debug() {
let my_string = CowStr::from_static("static string");
println!("static_string = {my_string:#?}");
let my_string = CowStr::from("dynamic string");
println!("dynamic_string = {my_string:#?}");
}
#[test]
fn to_cowstr() {
assert_eq!("".to_cowstr(), "");
assert_eq!('a'.to_cowstr(), "a");
assert_eq!(3.45.to_cowstr(), "3.45");
assert_eq!(false.to_cowstr(), "false");
assert_eq!(true.to_cowstr(), "true");
}
#[test]
fn smoke() {
let _empty_string = CowStr::new();
let _static_string = CowStr::from_static("smoke 1");
let _dynamic_string = CowStr::from("smoke 2");
}
#[test]
fn size() {
assert_eq!(std::mem::size_of::<CowStr>(), std::mem::size_of::<&str>());
}
#[test]
fn as_str_static() {
let my_string = CowStr::from_static("as_str_static");
assert_eq!(my_string.as_str(), "as_str_static");
}
#[test]
fn as_str_dynamic() {
let my_string = CowStr::from("as_str_dynamic");
let mut cloned = my_string.clone();
cloned.push_str(" foobar");
assert_eq!(my_string.as_str(), "as_str_dynamic");
assert_eq!(cloned.as_str(), "as_str_dynamic foobar");
}
#[test]
fn as_mut_str() {
let mut my_string = CowStr::from_static("as_mut_str");
assert_eq!(my_string.as_mut_str(), "as_mut_str");
}
#[test]
#[should_panic(expected = "Capacity overflow")]
fn over_capacity() {
let _boom = CowStr::with_capacity(usize::MAX);
}
#[test]
fn push() {
let mut my_string = CowStr::from_static("push");
my_string.push(' ');
assert_eq!(my_string, "push ");
my_string.push('1');
my_string.push('2');
my_string.push('3');
my_string.push('4');
my_string.push('5');
my_string.push('6');
my_string.push('7');
my_string.push('8');
assert_eq!(my_string, "push 12345678");
}
#[test]
fn push_str() {
let mut my_string = CowStr::from_static("push_str");
my_string.push_str(" foo");
assert_eq!(my_string, "push_str foo");
my_string.push_str(" bar");
assert_eq!(my_string, "push_str foo bar");
}
#[test]
fn clone_static() {
let my_string = CowStr::from_static("clone_static");
assert_eq!(my_string.strong_count(), None);
assert!(my_string.deref_static().is_ok());
let my_string2 = my_string.clone();
assert!(my_string.deref_static().is_ok());
assert_eq!(my_string2, "clone_static");
drop(my_string);
assert_eq!(my_string2, "clone_static");
}
#[test]
fn clone_dynamic() {
let my_string = CowStr::from("clone_dynamic");
assert_eq!(
my_string.strong_count(),
Some(NonZeroUsize::new(1).unwrap())
);
let my_string2 = my_string.clone();
assert_eq!(
my_string.strong_count(),
Some(NonZeroUsize::new(2).unwrap())
);
assert_eq!(
my_string2.strong_count(),
Some(NonZeroUsize::new(2).unwrap())
);
assert_eq!(my_string2, "clone_dynamic");
let my_string3 = my_string.clone();
assert_eq!(
my_string3.strong_count(),
Some(NonZeroUsize::new(3).unwrap())
);
drop(my_string);
assert_eq!(
my_string2.strong_count(),
Some(NonZeroUsize::new(2).unwrap())
);
}
#[test]
fn replicate() {
let my_string = CowStr::from("replicate");
assert_eq!(
my_string.strong_count(),
Some(NonZeroUsize::new(1).unwrap())
);
let my_string2 = my_string.replicate();
assert_eq!(
my_string.strong_count(),
Some(NonZeroUsize::new(1).unwrap())
);
assert_eq!(
my_string2.strong_count(),
Some(NonZeroUsize::new(1).unwrap())
);
assert_eq!(my_string, my_string2);
}
#[test]
fn cmp() {
let my_static_string = CowStr::from_static("cmp");
let my_dynamic_string = CowStr::from("cmp");
let my_dynamic_string2 = CowStr::from("other cmp");
assert_eq!(my_static_string, my_dynamic_string);
assert_ne!(my_dynamic_string, my_dynamic_string2);
}
#[test]
fn ord() {
let my_static_string = CowStr::from_static("a ord");
let my_dynamic_string = CowStr::from("b ord");
assert!(my_static_string < my_dynamic_string);
}
#[test]
fn as_ref() {
let my_string = CowStr::from("as_ref");
assert_eq!(&*my_string, <CowStr as AsRef<str>>::as_ref(&my_string));
assert_eq!(
my_string.as_bytes(),
<CowStr as AsRef<[u8]>>::as_ref(&my_string)
);
}
#[test]
#[allow(clippy::explicit_deref_methods)]
fn borrow() {
use std::ops::Deref;
let my_string = CowStr::from("borrow");
assert_eq!(
my_string.deref(),
<cowstr::CowStr as Borrow<str>>::borrow(&my_string)
);
}
#[test]
#[ignore]
fn print() {
let my_string = CowStr::from_static("static string");
println!("static_string = {my_string}");
let my_string = CowStr::from("dynamic string");
println!("dynamic_string = {my_string}");
}