1use alloc::borrow::Cow;
4use core::borrow::Borrow;
5use core::cmp::Ordering;
6use core::fmt::{self, Debug, Display, Formatter, Write};
7use core::hash::{Hash, Hasher};
8use core::ops::{Add, AddAssign, Deref};
9use core::str::FromStr;
10#[cfg(feature = "std")]
11use std::ffi::OsStr;
12#[cfg(feature = "std")]
13use std::path::Path;
14
15#[cfg(not(feature = "std"))]
16use alloc::string::String;
17
18use crate::bytes::{EcoBytes, InlineVec};
19use crate::EcoVec;
20
21#[macro_export]
27#[clippy::format_args]
28macro_rules! eco_format {
29 ($($tts:tt)*) => {{
30 use ::core::fmt::Write;
31 let mut s = $crate::EcoString::new();
32 ::core::write!(s, $($tts)*).unwrap();
33 s
34 }};
35}
36
37#[derive(Clone)]
70pub struct EcoString(EcoBytes);
71
72impl EcoString {
73 pub const INLINE_LIMIT: usize = EcoBytes::INLINE_LIMIT;
81
82 #[inline]
84 pub const fn new() -> Self {
85 Self(EcoBytes::new())
86 }
87
88 #[inline]
93 pub const fn inline(string: &str) -> Self {
94 Self(EcoBytes::inline(string.as_bytes()))
95 }
96
97 #[inline]
102 pub const fn try_inline(string: &str) -> Option<Self> {
103 match InlineVec::from_slice(string.as_bytes()) {
104 Ok(inline) => Some(Self(EcoBytes::from_inline(inline))),
105 Err(()) => None,
106 }
107 }
108
109 #[inline]
111 pub fn with_capacity(capacity: usize) -> Self {
112 Self(EcoBytes::with_capacity(capacity))
113 }
114
115 #[inline]
117 fn from_str(string: &str) -> Self {
118 Self(EcoBytes::from(string.as_bytes()))
119 }
120
121 #[inline]
123 pub fn is_empty(&self) -> bool {
124 self.len() == 0
125 }
126
127 #[inline]
129 pub fn len(&self) -> usize {
130 self.0.len()
131 }
132
133 #[inline]
138 pub fn capacity(&self) -> usize {
139 self.0.capacity()
140 }
141
142 #[inline]
144 pub fn is_inline(&self) -> bool {
145 self.0.is_inline()
146 }
147
148 #[inline]
150 pub fn as_str(&self) -> &str {
151 unsafe { core::str::from_utf8_unchecked(self.0.as_slice()) }
157 }
158
159 #[inline]
163 pub fn make_mut(&mut self) -> &mut str {
164 unsafe { core::str::from_utf8_unchecked_mut(self.0.make_mut()) }
170 }
171
172 #[inline]
174 pub fn push(&mut self, c: char) {
175 if c.len_utf8() == 1 {
176 self.0.push(c as u8);
177 } else {
178 self.push_str(c.encode_utf8(&mut [0; 4]));
179 }
180 }
181
182 #[inline]
184 pub fn pop(&mut self) -> Option<char> {
185 let slice = self.as_str();
186 let c = slice.chars().next_back()?;
187 self.0.truncate(slice.len() - c.len_utf8());
188 Some(c)
189 }
190
191 pub fn push_str(&mut self, string: &str) {
193 self.0.extend_from_slice(string.as_bytes());
194 }
195
196 pub fn insert(&mut self, index: usize, c: char) {
198 self.insert_str(index, c.encode_utf8(&mut [0; 4]));
199 }
200
201 pub fn insert_str(&mut self, index: usize, string: &str) {
203 assert!(self.is_char_boundary(index));
204 self.0.insert_slice(index, string.as_bytes());
205 }
206
207 pub fn remove(&mut self, index: usize) -> char {
209 assert!(self.is_char_boundary(index));
210 let char = self[index..].chars().next().unwrap();
211 self.0.remove_range(index..index + char.len_utf8());
212 char
213 }
214
215 pub fn replace(&self, pat: &str, to: &str) -> Self {
221 self.replacen(pat, to, usize::MAX)
222 }
223
224 pub fn replacen(&self, pat: &str, to: &str, count: usize) -> Self {
230 let mut result = Self::new();
232 let mut last_end = 0;
233 for (start, part) in self.match_indices(pat).take(count) {
234 result.push_str(unsafe { self.get_unchecked(last_end..start) });
236 result.push_str(to);
237 last_end = start + part.len();
238 }
239 result.push_str(unsafe { self.get_unchecked(last_end..self.len()) });
241 result
242 }
243
244 #[inline]
246 pub fn clear(&mut self) {
247 self.0.clear();
248 }
249
250 #[inline]
257 pub fn truncate(&mut self, new_len: usize) {
258 if new_len <= self.len() {
259 assert!(self.is_char_boundary(new_len));
260 self.0.truncate(new_len);
261 }
262 }
263
264 pub fn reserve(&mut self, additional: usize) {
269 self.0.reserve(additional);
270 }
271
272 pub fn to_lowercase(&self) -> Self {
274 let str = self.as_str();
275 let mut lower = Self::with_capacity(str.len());
276 for c in str.chars() {
277 if c == 'Σ' {
279 return str.to_lowercase().into();
280 }
281 for v in c.to_lowercase() {
282 lower.push(v);
283 }
284 }
285 lower
286 }
287
288 pub fn to_uppercase(&self) -> Self {
290 let str = self.as_str();
291 let mut upper = Self::with_capacity(str.len());
292 for c in str.chars() {
293 for v in c.to_uppercase() {
294 upper.push(v);
295 }
296 }
297 upper
298 }
299
300 pub fn to_ascii_lowercase(&self) -> Self {
303 let mut s = self.clone();
304 s.make_mut().make_ascii_lowercase();
305 s
306 }
307
308 pub fn to_ascii_uppercase(&self) -> Self {
311 let mut s = self.clone();
312 s.make_mut().make_ascii_uppercase();
313 s
314 }
315
316 pub fn repeat(&self, n: usize) -> Self {
318 Self(self.0.repeat(n))
319 }
320}
321
322impl Deref for EcoString {
323 type Target = str;
324
325 #[inline]
326 fn deref(&self) -> &str {
327 self.as_str()
328 }
329}
330
331impl Default for EcoString {
332 #[inline]
333 fn default() -> Self {
334 Self::new()
335 }
336}
337
338impl Debug for EcoString {
339 #[inline]
340 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
341 Debug::fmt(self.as_str(), f)
342 }
343}
344
345impl Display for EcoString {
346 #[inline]
347 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
348 Display::fmt(self.as_str(), f)
349 }
350}
351
352impl Eq for EcoString {}
353
354impl PartialEq for EcoString {
355 #[inline]
356 fn eq(&self, other: &Self) -> bool {
357 self.as_str().eq(other.as_str())
358 }
359}
360
361impl PartialEq<str> for EcoString {
362 #[inline]
363 fn eq(&self, other: &str) -> bool {
364 self.as_str().eq(other)
365 }
366}
367
368impl PartialEq<&str> for EcoString {
369 #[inline]
370 fn eq(&self, other: &&str) -> bool {
371 self.as_str().eq(*other)
372 }
373}
374
375impl PartialEq<String> for EcoString {
376 #[inline]
377 fn eq(&self, other: &String) -> bool {
378 self.as_str().eq(other)
379 }
380}
381
382impl PartialEq<EcoString> for str {
383 #[inline]
384 fn eq(&self, other: &EcoString) -> bool {
385 self.eq(other.as_str())
386 }
387}
388
389impl PartialEq<EcoString> for &str {
390 #[inline]
391 fn eq(&self, other: &EcoString) -> bool {
392 (*self).eq(other.as_str())
393 }
394}
395
396impl PartialEq<EcoString> for String {
397 #[inline]
398 fn eq(&self, other: &EcoString) -> bool {
399 self.eq(other.as_str())
400 }
401}
402
403impl Ord for EcoString {
404 #[inline]
405 fn cmp(&self, other: &Self) -> Ordering {
406 self.as_str().cmp(other.as_str())
407 }
408}
409
410impl PartialOrd for EcoString {
411 #[inline]
412 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
413 Some(self.cmp(other))
414 }
415}
416
417impl Hash for EcoString {
418 #[inline]
419 fn hash<H: Hasher>(&self, state: &mut H) {
420 self.as_str().hash(state);
421 }
422}
423
424impl Write for EcoString {
425 #[inline]
426 fn write_str(&mut self, s: &str) -> fmt::Result {
427 self.push_str(s);
428 Ok(())
429 }
430
431 #[inline]
432 fn write_char(&mut self, c: char) -> fmt::Result {
433 self.push(c);
434 Ok(())
435 }
436}
437
438impl Add for EcoString {
439 type Output = Self;
440
441 #[inline]
442 fn add(mut self, rhs: Self) -> Self::Output {
443 self += rhs;
444 self
445 }
446}
447
448impl AddAssign for EcoString {
449 #[inline]
450 fn add_assign(&mut self, rhs: Self) {
451 self.push_str(rhs.as_str());
452 }
453}
454
455impl Add<&str> for EcoString {
456 type Output = Self;
457
458 #[inline]
459 fn add(mut self, rhs: &str) -> Self::Output {
460 self += rhs;
461 self
462 }
463}
464
465impl AddAssign<&str> for EcoString {
466 #[inline]
467 fn add_assign(&mut self, rhs: &str) {
468 self.push_str(rhs);
469 }
470}
471
472impl AsRef<str> for EcoString {
473 #[inline]
474 fn as_ref(&self) -> &str {
475 self
476 }
477}
478
479impl Borrow<str> for EcoString {
480 #[inline]
481 fn borrow(&self) -> &str {
482 self
483 }
484}
485
486impl AsRef<[u8]> for EcoString {
487 #[inline]
488 fn as_ref(&self) -> &[u8] {
489 self.as_str().as_bytes()
490 }
491}
492
493#[cfg(feature = "std")]
494impl AsRef<OsStr> for EcoString {
495 #[inline]
496 fn as_ref(&self) -> &OsStr {
497 self.as_str().as_ref()
498 }
499}
500
501#[cfg(feature = "std")]
502impl AsRef<Path> for EcoString {
503 #[inline]
504 fn as_ref(&self) -> &Path {
505 self.as_str().as_ref()
506 }
507}
508
509impl From<char> for EcoString {
510 #[inline]
511 fn from(c: char) -> Self {
512 Self::inline(c.encode_utf8(&mut [0; 4]))
513 }
514}
515
516impl From<&str> for EcoString {
517 #[inline]
518 fn from(s: &str) -> Self {
519 Self::from_str(s)
520 }
521}
522
523impl From<String> for EcoString {
524 #[inline]
527 fn from(s: String) -> Self {
528 Self::from_str(&s)
529 }
530}
531
532impl From<&String> for EcoString {
533 #[inline]
534 fn from(s: &String) -> Self {
535 Self::from_str(s.as_str())
536 }
537}
538
539impl From<&EcoString> for EcoString {
540 #[inline]
541 fn from(s: &EcoString) -> Self {
542 s.clone()
543 }
544}
545
546impl From<Cow<'_, str>> for EcoString {
547 #[inline]
548 fn from(s: Cow<str>) -> Self {
549 Self::from_str(&s)
550 }
551}
552
553impl From<EcoString> for String {
554 #[inline]
556 fn from(s: EcoString) -> Self {
557 s.as_str().into()
558 }
559}
560
561impl From<&EcoString> for String {
562 #[inline]
563 fn from(s: &EcoString) -> Self {
564 s.as_str().into()
565 }
566}
567
568impl From<EcoString> for EcoBytes {
569 #[inline]
571 fn from(string: EcoString) -> Self {
572 string.0
573 }
574}
575
576impl From<EcoString> for EcoVec<u8> {
577 #[inline]
580 fn from(string: EcoString) -> Self {
581 string.0.into()
582 }
583}
584
585impl FromIterator<char> for EcoString {
586 #[inline]
587 fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
588 let mut s = Self::new();
589 for c in iter {
590 s.push(c);
591 }
592 s
593 }
594}
595
596impl<'a> FromIterator<&'a str> for EcoString {
597 #[inline]
598 fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
599 let mut buf = Self::new();
600 buf.extend(iter);
601 buf
602 }
603}
604
605impl FromIterator<Self> for EcoString {
606 #[inline]
607 fn from_iter<T: IntoIterator<Item = Self>>(iter: T) -> Self {
608 let mut s = Self::new();
609 for piece in iter {
610 s.push_str(&piece);
611 }
612 s
613 }
614}
615
616impl Extend<char> for EcoString {
617 #[inline]
618 fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
619 for c in iter {
620 self.push(c);
621 }
622 }
623}
624
625impl<'a> Extend<&'a str> for EcoString {
626 #[inline]
627 fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
628 iter.into_iter().for_each(move |s| self.push_str(s));
629 }
630}
631
632impl TryFrom<EcoBytes> for EcoString {
633 type Error = core::str::Utf8Error;
634
635 #[inline]
637 fn try_from(bytes: EcoBytes) -> Result<Self, Self::Error> {
638 core::str::from_utf8(&bytes)?;
639 Ok(Self(bytes))
640 }
641}
642
643impl TryFrom<EcoVec<u8>> for EcoString {
644 type Error = core::str::Utf8Error;
645
646 #[inline]
648 fn try_from(bytes: EcoVec<u8>) -> Result<Self, Self::Error> {
649 Self::try_from(EcoBytes::from(bytes))
650 }
651}
652
653impl FromStr for EcoString {
654 type Err = core::convert::Infallible;
655
656 #[inline]
657 fn from_str(s: &str) -> Result<Self, Self::Err> {
658 Ok(Self::from_str(s))
659 }
660}
661
662pub trait ToEcoString {
667 fn to_eco_string(&self) -> EcoString;
669}
670
671impl<T: Display + ?Sized> ToEcoString for T {
672 fn to_eco_string(&self) -> EcoString {
673 eco_format!("{self}")
674 }
675}
676
677#[cfg(feature = "serde")]
678mod serde {
679 use super::EcoString;
680
681 use core::fmt;
682 use serde::de::{Deserializer, Error, Unexpected, Visitor};
683
684 impl serde::Serialize for EcoString {
685 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
686 where
687 S: serde::Serializer,
688 {
689 self.as_str().serialize(serializer)
690 }
691 }
692
693 impl<'de> serde::Deserialize<'de> for EcoString {
694 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
695 where
696 D: Deserializer<'de>,
697 {
698 deserializer.deserialize_str(EcoStringVisitor)
699 }
700 }
701
702 struct EcoStringVisitor;
703
704 impl Visitor<'_> for EcoStringVisitor {
705 type Value = EcoString;
706
707 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
708 formatter.write_str("a string")
709 }
710
711 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
712 where
713 E: Error,
714 {
715 Ok(EcoString::from(v))
716 }
717
718 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
719 where
720 E: Error,
721 {
722 if let Ok(utf8) = core::str::from_utf8(v) {
723 return Ok(EcoString::from(utf8));
724 }
725 Err(Error::invalid_value(Unexpected::Bytes(v), &self))
726 }
727 }
728}