use core::borrow::Borrow;
use core::cmp::Ordering;
extern crate alloc;
use alloc::borrow::Cow;
use alloc::string::String;
use core::fmt::{self, Debug, Display, Formatter, Write};
use core::hash::{Hash, Hasher};
use core::ops::{Add, AddAssign, Deref};
use core::str::FromStr;
use crate::allocator::Global;
use crate::dynamic::{DynamicVec, InlineVec, LIMIT};
#[macro_export]
macro_rules! eco_format {
($($tts:tt)*) => {
$crate::EcoString::_format(::core::format_args!($($tts)*))
};
}
#[derive(Clone)]
pub struct EcoStr<'a>(DynamicVec<'a>);
#[derive(Clone)]
pub struct EcoString(EcoStr<'static>);
impl<'a> EcoStr<'a> {
pub const INLINE_LIMIT: usize = LIMIT;
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn as_str(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(self.0.as_slice()) }
}
#[inline]
pub fn make_mut(&mut self) -> &mut str {
unsafe { core::str::from_utf8_unchecked_mut(self.0.make_mut(0)) }
}
#[inline]
pub fn push(&mut self, c: char) {
if c.len_utf8() == 1 {
self.0.push(c as u8);
} else {
self.push_str(c.encode_utf8(&mut [0; 4]));
}
}
#[inline]
pub fn push_str(&mut self, string: &str) {
self.0.extend_from_slice(string.as_bytes());
}
#[inline]
pub fn pop(&mut self) -> Option<char> {
let slice = self.as_str();
let c = slice.chars().next_back()?;
self.0.truncate(slice.len() - c.len_utf8());
Some(c)
}
#[inline]
pub fn clear(&mut self) {
self.0.clear();
}
#[inline]
#[track_caller]
pub fn truncate(&mut self, new_len: usize) {
if new_len <= self.len() {
assert!(self.is_char_boundary(new_len));
self.0.truncate(new_len);
}
}
#[track_caller]
pub fn insert_str(&mut self, index: usize, string: &str) {
assert!(self.is_char_boundary(index), "insertion index is not a char boundary");
let old_len = self.len();
let new_len = old_len + string.len();
let mut result = EcoString::with_capacity(new_len);
result.push_str(unsafe { self.get_unchecked(..index) });
result.push_str(string);
result.push_str(unsafe { self.get_unchecked(index..) });
*self = result.into_raw_eco_str();
}
#[inline]
#[track_caller]
pub fn insert(&mut self, index: usize, c: char) {
self.insert_str(index, c.encode_utf8(&mut [0; 4]));
}
#[track_caller]
pub fn remove(&mut self, index: usize) -> char {
assert!(self.is_char_boundary(index), "removal index is not a char boundary");
let c = unsafe { self.get_unchecked(index..) }
.chars()
.next()
.expect("removal index is out of bounds");
let char_end = index + c.len_utf8();
let old_len = self.len();
let mut result = EcoString::with_capacity(old_len - c.len_utf8());
result.push_str(unsafe { self.get_unchecked(..index) });
result.push_str(unsafe { self.get_unchecked(char_end..) });
*self = result.into_raw_eco_str();
c
}
#[inline]
pub fn into_owned(self) -> EcoString {
EcoString(EcoStr(self.0.to_owned()))
}
pub fn replace(&self, pat: &str, to: &str) -> EcoString {
self.replacen(pat, to, usize::MAX)
}
pub fn replacen(&self, pat: &str, to: &str, count: usize) -> EcoString {
let mut result = EcoString::with_capacity(self.len());
let mut last_end = 0;
for (start, part) in self.match_indices(pat).take(count) {
result.push_str(unsafe { self.get_unchecked(last_end..start) });
result.push_str(to);
last_end = start + part.len();
}
result.push_str(unsafe { self.get_unchecked(last_end..self.len()) });
result
}
pub fn to_lowercase(&self) -> EcoString {
let str = self.as_str();
let bytes = str.as_bytes();
let mut lower = EcoString::with_capacity(str.len());
let mut ascii_end = 0;
for &b in bytes {
if b.is_ascii() {
ascii_end += 1;
} else {
break;
}
}
if ascii_end > 0 {
lower.push_str(unsafe { str.get_unchecked(..ascii_end) });
lower.make_mut().make_ascii_lowercase();
}
for c in unsafe { str.get_unchecked(ascii_end..) }.chars() {
if c == 'Σ' {
return str.to_lowercase().into();
}
for v in c.to_lowercase() {
lower.push(v);
}
}
lower
}
pub fn to_uppercase(&self) -> EcoString {
let str = self.as_str();
let bytes = str.as_bytes();
let mut upper = EcoString::with_capacity(str.len());
let mut ascii_end = 0;
for &b in bytes {
if b.is_ascii() {
ascii_end += 1;
} else {
break;
}
}
if ascii_end > 0 {
upper.push_str(unsafe { str.get_unchecked(..ascii_end) });
upper.make_mut().make_ascii_uppercase();
}
for c in unsafe { str.get_unchecked(ascii_end..) }.chars() {
for v in c.to_uppercase() {
upper.push(v);
}
}
upper
}
pub fn to_ascii_lowercase(&self) -> EcoString {
let mut s = self.clone().into_owned();
s.make_mut().make_ascii_lowercase();
s
}
pub fn to_ascii_uppercase(&self) -> EcoString {
let mut s = self.clone().into_owned();
s.make_mut().make_ascii_uppercase();
s
}
pub fn repeat(&self, n: usize) -> EcoString {
let slice = self.as_bytes();
let capacity = slice.len().saturating_mul(n);
let mut vec = DynamicVec::with_capacity(capacity);
for _ in 0..n {
vec.extend_from_slice(slice);
}
EcoString(EcoStr(vec))
}
#[inline]
#[allow(dead_code)]
pub(crate) fn from_raw(vec: DynamicVec<'a>) -> Self {
Self(vec)
}
#[inline]
#[allow(dead_code)]
pub(crate) fn into_raw(self) -> DynamicVec<'a> {
self.0
}
}
impl EcoString {
pub const INLINE_LIMIT: usize = LIMIT;
#[inline]
pub const fn new() -> Self {
Self(EcoStr(DynamicVec::new()))
}
#[inline]
pub const fn inline(string: &str) -> Self {
let Ok(inline) = InlineVec::from_slice(string.as_bytes()) else {
exceeded_inline_capacity();
};
Self(EcoStr(DynamicVec::from_inline(inline)))
}
#[inline]
pub fn from_static(string: &'static str) -> Self {
Self(EcoStr(DynamicVec::from_slice_in(string.as_bytes(), Global)))
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self(EcoStr(DynamicVec::with_capacity(capacity)))
}
#[doc(hidden)]
#[inline]
#[must_use]
pub fn _format(args: fmt::Arguments<'_>) -> Self {
#[cfg(feature = "nightly-fmt")]
if let Some(s) = args.as_str() {
return Self::from(s);
}
let mut s = Self::new();
s.write_fmt(args).unwrap();
s
}
#[inline]
#[allow(clippy::should_implement_trait)]
pub fn from_str(string: &str) -> Self {
Self(EcoStr(DynamicVec::from_slice(string.as_bytes())))
}
#[inline]
pub(crate) fn from_raw(vec: DynamicVec<'static>) -> Self {
Self(EcoStr(vec))
}
#[inline]
pub(crate) fn into_raw(self) -> DynamicVec<'static> {
(self.0).0
}
#[inline]
fn into_raw_eco_str(self) -> EcoStr<'static> {
self.0
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
#[inline]
pub fn make_mut(&mut self) -> &mut str {
self.0.make_mut()
}
#[inline]
pub fn push(&mut self, c: char) {
self.0.push(c);
}
#[inline]
pub fn push_str(&mut self, string: &str) {
self.0.push_str(string);
}
#[inline]
#[track_caller]
pub fn insert(&mut self, index: usize, c: char) {
self.0.insert(index, c);
}
#[inline]
#[track_caller]
pub fn insert_str(&mut self, index: usize, string: &str) {
self.0.insert_str(index, string);
}
#[inline]
pub fn pop(&mut self) -> Option<char> {
self.0.pop()
}
#[inline]
pub fn clear(&mut self) {
self.0.clear();
}
#[inline]
#[track_caller]
pub fn truncate(&mut self, new_len: usize) {
self.0.truncate(new_len);
}
#[inline]
#[track_caller]
pub fn remove(&mut self, index: usize) -> char {
self.0.remove(index)
}
#[inline]
pub fn replace(&self, pat: &str, to: &str) -> Self {
self.0.replace(pat, to)
}
#[inline]
pub fn replacen(&self, pat: &str, to: &str, count: usize) -> Self {
self.0.replacen(pat, to, count)
}
#[inline]
pub fn to_lowercase(&self) -> Self {
self.0.to_lowercase()
}
#[inline]
pub fn to_uppercase(&self) -> Self {
self.0.to_uppercase()
}
#[inline]
pub fn to_ascii_lowercase(&self) -> Self {
self.0.to_ascii_lowercase()
}
#[inline]
pub fn to_ascii_uppercase(&self) -> Self {
self.0.to_ascii_uppercase()
}
#[inline]
pub fn repeat(&self, n: usize) -> Self {
self.0.repeat(n)
}
}
impl Deref for EcoStr<'_> {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.as_str()
}
}
impl Deref for EcoString {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.0.as_str()
}
}
impl Default for EcoStr<'_> {
#[inline]
fn default() -> Self {
EcoStr(DynamicVec::new())
}
}
impl Debug for EcoStr<'_> {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(self.as_str(), f)
}
}
impl Display for EcoStr<'_> {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self.as_str(), f)
}
}
impl Eq for EcoStr<'_> {}
impl PartialEq for EcoStr<'_> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl PartialEq<str> for EcoStr<'_> {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_str().eq(other)
}
}
impl PartialEq<&str> for EcoStr<'_> {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.as_str().eq(*other)
}
}
impl PartialEq<String> for EcoStr<'_> {
#[inline]
fn eq(&self, other: &String) -> bool {
self.as_str().eq(other.as_str())
}
}
impl PartialEq<EcoStr<'_>> for str {
#[inline]
fn eq(&self, other: &EcoStr<'_>) -> bool {
self.eq(other.as_str())
}
}
impl PartialEq<EcoStr<'_>> for &str {
#[inline]
fn eq(&self, other: &EcoStr<'_>) -> bool {
(*self).eq(other.as_str())
}
}
impl PartialEq<EcoStr<'_>> for String {
#[inline]
fn eq(&self, other: &EcoStr<'_>) -> bool {
self.as_str().eq(other.as_str())
}
}
impl Ord for EcoStr<'_> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl PartialOrd for EcoStr<'_> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Hash for EcoStr<'_> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
impl Write for EcoStr<'_> {
#[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(())
}
}
impl AsRef<str> for EcoStr<'_> {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for EcoStr<'_> {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl AsRef<[u8]> for EcoStr<'_> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_str().as_bytes()
}
}
impl<'a> From<&'a str> for EcoStr<'a> {
#[inline]
fn from(s: &'a str) -> Self {
Self(DynamicVec::from_slice_in(s.as_bytes(), Global))
}
}
impl Default for EcoString {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Debug for EcoString {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(self.as_str(), f)
}
}
impl Display for EcoString {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self.as_str(), f)
}
}
impl Eq for EcoString {}
impl PartialEq for EcoString {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl PartialEq<str> for EcoString {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_str().eq(other)
}
}
impl PartialEq<&str> for EcoString {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.as_str().eq(*other)
}
}
impl PartialEq<String> for EcoString {
#[inline]
fn eq(&self, other: &String) -> bool {
self.as_str().eq(other.as_str())
}
}
impl PartialEq<EcoString> for str {
#[inline]
fn eq(&self, other: &EcoString) -> bool {
self.eq(other.as_str())
}
}
impl PartialEq<EcoString> for &str {
#[inline]
fn eq(&self, other: &EcoString) -> bool {
(*self).eq(other.as_str())
}
}
impl PartialEq<EcoString> for String {
#[inline]
fn eq(&self, other: &EcoString) -> bool {
self.as_str().eq(other.as_str())
}
}
impl PartialEq<EcoString> for EcoStr<'_> {
#[inline]
fn eq(&self, other: &EcoString) -> bool {
self.as_str().eq(other.as_str())
}
}
impl PartialEq<EcoStr<'_>> for EcoString {
#[inline]
fn eq(&self, other: &EcoStr<'_>) -> bool {
self.as_str().eq(other.as_str())
}
}
impl Ord for EcoString {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl PartialOrd for EcoString {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Hash for EcoString {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
impl Write for EcoString {
#[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(())
}
}
impl Add for EcoString {
type Output = Self;
#[inline]
fn add(mut self, rhs: Self) -> Self::Output {
self += rhs;
self
}
}
impl AddAssign for EcoString {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.push_str(rhs.as_str());
}
}
impl Add<&str> for EcoString {
type Output = Self;
#[inline]
fn add(mut self, rhs: &str) -> Self::Output {
self += rhs;
self
}
}
impl AddAssign<&str> for EcoString {
#[inline]
fn add_assign(&mut self, rhs: &str) {
self.push_str(rhs);
}
}
impl AsRef<str> for EcoString {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for EcoString {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl AsRef<[u8]> for EcoString {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_str().as_bytes()
}
}
#[cfg(feature = "std")]
impl AsRef<std::ffi::OsStr> for EcoString {
#[inline]
fn as_ref(&self) -> &std::ffi::OsStr {
self.as_str().as_ref()
}
}
#[cfg(feature = "std")]
impl AsRef<std::path::Path> for EcoString {
#[inline]
fn as_ref(&self) -> &std::path::Path {
self.as_str().as_ref()
}
}
impl From<char> for EcoString {
#[inline]
fn from(c: char) -> Self {
Self::inline(c.encode_utf8(&mut [0; 4]))
}
}
impl From<&str> for EcoString {
#[inline]
fn from(s: &str) -> Self {
Self::from_str(s)
}
}
impl From<String> for EcoString {
#[inline]
fn from(s: String) -> Self {
if s.len() <= LIMIT {
Self::from_str(&s)
} else {
let bytes = s.into_bytes();
let eco: crate::EcoVec<u8> = crate::EcoVec::from(bytes);
let dv = DynamicVec::from_eco(eco);
EcoString(EcoStr(dv))
}
}
}
impl From<&String> for EcoString {
#[inline]
fn from(s: &String) -> Self {
Self::from_str(s.as_str())
}
}
impl From<&EcoString> for EcoString {
#[inline]
fn from(s: &EcoString) -> Self {
s.clone()
}
}
impl From<Cow<'_, str>> for EcoString {
#[inline]
fn from(s: Cow<'_, str>) -> Self {
Self::from_str(&s)
}
}
impl FromIterator<char> for EcoString {
#[inline]
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
let mut s = Self::new();
for c in iter {
s.push(c);
}
s
}
}
impl FromIterator<Self> for EcoString {
#[inline]
fn from_iter<T: IntoIterator<Item = Self>>(iter: T) -> Self {
let mut s = Self::new();
for piece in iter {
s.push_str(&piece);
}
s
}
}
impl<'a> FromIterator<&'a str> for EcoString {
#[inline]
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
let mut buf = Self::new();
buf.extend(iter);
buf
}
}
impl Extend<char> for EcoString {
#[inline]
fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
for c in iter {
self.push(c);
}
}
}
impl<'a> Extend<&'a str> for EcoString {
#[inline]
fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
iter.into_iter().for_each(move |s| self.push_str(s));
}
}
impl From<EcoString> for String {
#[inline]
fn from(s: EcoString) -> Self {
s.as_str().into()
}
}
impl From<&EcoString> for String {
#[inline]
fn from(s: &EcoString) -> Self {
s.as_str().into()
}
}
impl FromStr for EcoString {
type Err = core::convert::Infallible;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::from_str(s))
}
}
pub trait ToEcoString {
fn to_eco_string(&self) -> EcoString;
}
impl<T: core::fmt::Display + ?Sized> ToEcoString for T {
#[inline]
fn to_eco_string(&self) -> EcoString {
crate::eco_format!("{self}")
}
}
#[cold]
const fn exceeded_inline_capacity() -> ! {
panic!("exceeded inline capacity");
}
#[cfg(feature = "serde")]
mod serde {
use crate::EcoString;
use core::fmt;
use serde::de::{Deserializer, Error, Unexpected, Visitor};
impl serde::Serialize for EcoString {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.as_str().serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for EcoString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct EcoStringVisitor;
impl Visitor<'_> for EcoStringVisitor {
type Value = EcoString;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
Ok(EcoString::from(v))
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: Error,
{
if let Ok(utf8) = core::str::from_utf8(v) {
return Ok(EcoString::from(utf8));
}
Err(Error::invalid_value(Unexpected::Bytes(v), &self))
}
}
deserializer.deserialize_str(EcoStringVisitor)
}
}
}