lean_string/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![no_std]
4
5extern crate alloc;
6
7#[cfg(feature = "std")]
8extern crate std;
9
10use core::{
11    borrow::Borrow,
12    cmp, fmt,
13    hash::{Hash, Hasher},
14    ops::{Add, AddAssign, Deref},
15    str,
16    str::FromStr,
17};
18
19use alloc::{borrow::Cow, boxed::Box, string::String};
20
21#[cfg(feature = "std")]
22use std::ffi::OsStr;
23
24mod repr;
25use repr::Repr;
26
27mod errors;
28pub use errors::*;
29
30mod traits;
31pub use traits::ToLeanString;
32
33mod features;
34
35/// Compact, clone-on-write, UTF-8 encoded, growable string type.
36#[repr(transparent)]
37pub struct LeanString(Repr);
38
39fn _static_assert() {
40    const {
41        assert!(size_of::<LeanString>() == 2 * size_of::<usize>());
42        assert!(size_of::<Option<LeanString>>() == 2 * size_of::<usize>());
43        assert!(align_of::<LeanString>() == align_of::<usize>());
44        assert!(align_of::<Option<LeanString>>() == align_of::<usize>());
45    }
46}
47
48impl LeanString {
49    /// Creates a new empty [`LeanString`].
50    ///
51    /// Same as [`String::new()`], this will not allocate on the heap.
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// # use lean_string::LeanString;
57    /// let s = LeanString::new();
58    /// assert!(s.is_empty());
59    /// assert!(!s.is_heap_allocated());
60    /// ```
61    #[inline]
62    pub const fn new() -> Self {
63        LeanString(Repr::new())
64    }
65
66    /// Creates a new [`LeanString`] from a `&'static str`.
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// # use lean_string::LeanString;
72    /// let s = LeanString::from_static_str("Long text but static lifetime");
73    /// assert_eq!(s.as_str(), "Long text but static lifetime");
74    /// assert_eq!(s.len(), 29);
75    /// assert!(!s.is_heap_allocated());
76    /// ```
77    #[inline]
78    pub const fn from_static_str(text: &'static str) -> Self {
79        match Repr::from_static_str(text) {
80            Ok(repr) => LeanString(repr),
81            Err(_) => panic!("text is too long"),
82        }
83    }
84
85    /// Creates a new empty [`LeanString`] with at least capacity bytes.
86    ///
87    /// A [`LeanString`] will inline strings if the length is less than or equal to
88    /// `2 * size_of::<usize>()` bytes. This means that the minimum capacity of a [`LeanString`]
89    /// is `2 * size_of::<usize>()` bytes.
90    ///
91    /// # Panics
92    ///
93    /// Panics if **any** of the following conditions is met:
94    ///
95    /// - The system is out-of-memory.
96    /// - On 64-bit architecture, the `capacity` is greater than `2^56 - 1`.
97    /// - On 32-bit architecture, the `capacity` is greater than `2^32 - 1`.
98    ///
99    /// If you want to handle such a problem manually, use [`LeanString::try_with_capacity()`].
100    ///
101    /// # Examples
102    ///
103    /// ## inline capacity
104    ///
105    /// ```
106    /// # use lean_string::LeanString;
107    /// let s = LeanString::with_capacity(4);
108    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
109    /// assert!(!s.is_heap_allocated());
110    /// ```
111    ///
112    /// ## heap capacity
113    ///
114    /// ```
115    /// # use lean_string::LeanString;
116    /// let s = LeanString::with_capacity(100);
117    /// assert_eq!(s.capacity(), 100);
118    /// assert!(s.is_heap_allocated());
119    /// ```
120    #[inline]
121    pub fn with_capacity(capacity: usize) -> Self {
122        LeanString::try_with_capacity(capacity).unwrap_with_msg()
123    }
124
125    /// Fallible version of [`LeanString::with_capacity()`].
126    ///
127    /// This method won't panic if the system is out of memory, or if the `capacity` is too large, but
128    /// returns a [`ReserveError`]. Otherwise it behaves the same as [`LeanString::with_capacity()`].
129    #[inline]
130    pub fn try_with_capacity(capacity: usize) -> Result<Self, ReserveError> {
131        Repr::with_capacity(capacity).map(LeanString)
132    }
133
134    /// Converts a slice of bytes to a [`LeanString`].
135    ///
136    /// If the slice is not valid UTF-8, an error is returned.
137    ///
138    /// # Examples
139    ///
140    /// ## valid UTF-8
141    ///
142    /// ```
143    /// # use lean_string::LeanString;
144    /// let bytes = vec![240, 159, 166, 128];
145    /// let string = LeanString::from_utf8(&bytes).expect("valid UTF-8");
146    ///
147    /// assert_eq!(string, "🦀");
148    /// ```
149    ///
150    /// ## invalid UTF-8
151    ///
152    /// ```
153    /// # use lean_string::LeanString;
154    /// let bytes = &[255, 255, 255];
155    /// let result = LeanString::from_utf8(bytes);
156    ///
157    /// assert!(result.is_err());
158    /// ```
159    #[inline]
160    pub fn from_utf8(buf: &[u8]) -> Result<Self, str::Utf8Error> {
161        let str = str::from_utf8(buf)?;
162        Ok(LeanString::from(str))
163    }
164
165    /// Converts a slice of bytes to a [`LeanString`], including invalid characters.
166    ///
167    /// During this conversion, all invalid characters are replaced with the
168    /// [`char::REPLACEMENT_CHARACTER`].
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// # use lean_string::LeanString;
174    /// let invalid_bytes = b"Hello \xF0\x90\x80World";
175    /// let string = LeanString::from_utf8_lossy(invalid_bytes);
176    ///
177    /// assert_eq!(string, "Hello �World");
178    /// ```
179    #[inline]
180    pub fn from_utf8_lossy(buf: &[u8]) -> Self {
181        let mut ret = LeanString::with_capacity(buf.len());
182        for chunk in buf.utf8_chunks() {
183            ret.push_str(chunk.valid());
184            if !chunk.invalid().is_empty() {
185                ret.push(char::REPLACEMENT_CHARACTER);
186            }
187        }
188        ret
189    }
190
191    /// Converts a slice of bytes to a [`LeanString`] without checking if the bytes are valid
192    /// UTF-8.
193    ///
194    /// # Safety
195    ///
196    /// This function is unsafe because it does not check that the bytes passed to it are valid
197    /// UTF-8. If this constraint is violated, it may cause memory unsafety issues.
198    #[inline]
199    pub unsafe fn from_utf8_unchecked(buf: &[u8]) -> Self {
200        let str = unsafe { str::from_utf8_unchecked(buf) };
201        LeanString::from(str)
202    }
203
204    /// Decodes a slice of UTF-16 encoded bytes to a [`LeanString`], returning an error if `buf`
205    /// contains any invalid code points.
206    ///
207    /// # Examples
208    ///
209    /// ## valid UTF-16
210    ///
211    /// ```
212    /// # use lean_string::LeanString;
213    /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
214    /// assert_eq!(LeanString::from_utf16(v).unwrap(), "𝄞music");
215    /// ```
216    ///
217    /// ## invalid UTF-16
218    ///
219    /// ```
220    /// # use lean_string::LeanString;
221    /// // 𝄞mu<invalid>ic
222    /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
223    /// assert!(LeanString::from_utf16(v).is_err());
224    /// ```
225    #[inline]
226    pub fn from_utf16(buf: &[u16]) -> Result<Self, FromUtf16Error> {
227        let mut ret = LeanString::with_capacity(buf.len());
228        for c in char::decode_utf16(buf.iter().copied()) {
229            match c {
230                Ok(c) => ret.push(c),
231                Err(_) => return Err(FromUtf16Error),
232            }
233        }
234        Ok(ret)
235    }
236
237    /// Decodes a slice of UTF-16 encoded bytes to a [`LeanString`], replacing invalid code points
238    /// with the [`char::REPLACEMENT_CHARACTER`].
239    ///
240    /// # Examples
241    ///
242    /// ```
243    /// # use lean_string::LeanString;
244    /// // 𝄞mus<invalid>ic<invalid>
245    /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834];
246    /// assert_eq!(LeanString::from_utf16_lossy(v), "𝄞mus\u{FFFD}ic\u{FFFD}");
247    /// ```
248    #[inline]
249    pub fn from_utf16_lossy(buf: &[u16]) -> Self {
250        char::decode_utf16(buf.iter().copied())
251            .map(|c| c.unwrap_or(char::REPLACEMENT_CHARACTER))
252            .collect()
253    }
254
255    /// Returns the length of the string in bytes, not [`char`] or graphemes.
256    ///
257    /// # Examples
258    ///
259    /// ```
260    /// # use lean_string::LeanString;
261    /// let a = LeanString::from("foo");
262    /// assert_eq!(a.len(), 3);
263    ///
264    /// let fancy_f = LeanString::from("ƒoo");
265    /// assert_eq!(fancy_f.len(), 4);
266    /// assert_eq!(fancy_f.chars().count(), 3);
267    /// ```
268    #[inline]
269    pub fn len(&self) -> usize {
270        self.0.len()
271    }
272
273    /// Returns `true` if the [`LeanString`] has a length of 0, `false` otherwise
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// # use lean_string::LeanString;
279    /// let mut s = LeanString::new();
280    /// assert!(s.is_empty());
281    ///
282    /// s.push('a');
283    /// assert!(!s.is_empty());
284    /// ```
285    #[inline]
286    pub fn is_empty(&self) -> bool {
287        self.0.is_empty()
288    }
289
290    /// Returns the capacity of the [`LeanString`], in bytes.
291    ///
292    /// A [`LeanString`] will inline strings if the length is less than or equal to
293    /// `2 * size_of::<usize>()` bytes. This means that the minimum capacity of a [`LeanString`]
294    /// is `2 * size_of::<usize>()` bytes.
295    ///
296    /// # Examples
297    ///
298    /// ## inline capacity
299    ///
300    /// ```
301    /// # use lean_string::LeanString;
302    /// let s = LeanString::new();
303    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
304    /// ```
305    ///
306    /// ## heap capacity
307    ///
308    /// ```
309    /// # use lean_string::LeanString;
310    /// let s = LeanString::with_capacity(100);
311    /// assert_eq!(s.capacity(), 100);
312    /// ```
313    #[inline]
314    pub fn capacity(&self) -> usize {
315        self.0.capacity()
316    }
317
318    /// Returns a string slice containing the entire [`LeanString`].
319    ///
320    /// # Examples
321    ///
322    /// ```
323    /// # use lean_string::LeanString;
324    /// let s = LeanString::from("foo");
325    /// assert_eq!(s.as_str(), "foo");
326    /// ```
327    #[inline]
328    pub fn as_str(&self) -> &str {
329        self.0.as_str()
330    }
331
332    /// Returns a byte slice containing the entire [`LeanString`].
333    ///
334    /// # Examples
335    ///
336    /// ```
337    /// # use lean_string::LeanString;
338    /// let s = LeanString::from("hello");
339    /// assert_eq!(&[104, 101, 108, 108, 111], s.as_bytes());
340    /// ```
341    #[inline]
342    pub fn as_bytes(&self) -> &[u8] {
343        self.0.as_bytes()
344    }
345
346    /// Reserves capacity for at least `additional` bytes more than the current length.
347    ///
348    /// # Note
349    ///
350    /// This method clones the [`LeanString`] if it is not unique.
351    ///
352    /// # Panics
353    ///
354    /// Panics if **any** of the following conditions is met:
355    ///
356    /// - The system is out-of-memory.
357    /// - On 64-bit architecture, the `capacity` is greater than `2^56 - 1`.
358    /// - On 32-bit architecture, the `capacity` is greater than `2^32 - 1`.
359    ///
360    /// If you want to handle such a problem manually, use [`LeanString::try_reserve()`].
361    ///
362    /// # Examples
363    ///
364    /// ```
365    /// # use lean_string::LeanString;
366    /// let mut s = LeanString::new();
367    ///
368    /// // We have an inline storage on the stack.
369    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
370    /// assert!(!s.is_heap_allocated());
371    ///
372    /// s.reserve(100);
373    ///
374    /// // Now we have a heap storage.
375    /// assert!(s.capacity() >= s.len() + 100);
376    /// assert!(s.is_heap_allocated());
377    /// ```
378    #[inline]
379    pub fn reserve(&mut self, additional: usize) {
380        self.try_reserve(additional).unwrap_with_msg()
381    }
382
383    /// Fallible version of [`LeanString::reserve()`].
384    ///
385    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
386    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::reserve()`].
387    #[inline]
388    pub fn try_reserve(&mut self, additional: usize) -> Result<(), ReserveError> {
389        self.0.reserve(additional)
390    }
391
392    /// Shrinks the capacity of the [`LeanString`] to match its length.
393    ///
394    /// The resulting capacity is always greater than `2 * size_of::<usize>()` bytes because
395    /// [`LeanString`] has inline (on the stack) storage.
396    ///
397    /// # Note
398    ///
399    /// This method clones the [`LeanString`] if it is not unique and its capacity is greater than
400    /// its length.
401    ///
402    /// # Panics
403    ///
404    /// Panics if cloning the [`LeanString`] fails due to the system being out-of-memory. If you
405    /// want to handle such a problem manually, use [`LeanString::try_shrink_to_fit()`].
406    ///
407    /// # Examples
408    ///
409    /// ## short string
410    ///
411    /// ```
412    /// # use lean_string::LeanString;
413    /// let mut s = LeanString::from("foo");
414    ///
415    /// s.reserve(100);
416    /// assert_eq!(s.capacity(), 3 + 100);
417    ///
418    /// s.shrink_to_fit();
419    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
420    /// ```
421    ///
422    /// ## long string
423    ///
424    /// ```
425    /// # use lean_string::LeanString;
426    /// let mut s = LeanString::from("This is a text the length is more than 16 bytes");
427    ///
428    /// s.reserve(100);
429    /// assert!(s.capacity() > 16 + 100);
430    ///
431    /// s.shrink_to_fit();
432    /// assert_eq!(s.capacity(), s.len());
433    /// ```
434    #[inline]
435    pub fn shrink_to_fit(&mut self) {
436        self.try_shrink_to_fit().unwrap_with_msg()
437    }
438
439    /// Fallible version of [`LeanString::shrink_to_fit()`].
440    ///
441    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
442    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::shrink_to_fit()`].
443    #[inline]
444    pub fn try_shrink_to_fit(&mut self) -> Result<(), ReserveError> {
445        self.0.shrink_to(0)
446    }
447
448    /// Shrinks the capacity of the [`LeanString`] with a lower bound.
449    ///
450    /// The resulting capacity is always greater than `2 * size_of::<usize>()` bytes because the
451    /// [`LeanString`] has inline (on the stack) storage.
452    ///
453    /// # Note
454    ///
455    /// This method clones the [`LeanString`] if it is not unique and its capacity will be changed.
456    ///
457    /// # Panics
458    ///
459    /// Panics if cloning the [`LeanString`] fails due to the system being out-of-memory. If you
460    /// want to handle such a problem manually, use [`LeanString::try_shrink_to()`].
461    ///
462    /// # Examples
463    ///
464    /// ```
465    /// # use lean_string::LeanString;
466    /// let mut s = LeanString::with_capacity(100);
467    /// assert_eq!(s.capacity(), 100);
468    ///
469    /// // if the capacity was already bigger than the argument and unique, the call is no-op.
470    /// s.shrink_to(100);
471    /// assert_eq!(s.capacity(), 100);
472    ///
473    /// s.shrink_to(50);
474    /// assert_eq!(s.capacity(), 50);
475    ///
476    /// // if the string can be inlined, it is
477    /// s.shrink_to(10);
478    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
479    /// ```
480    #[inline]
481    pub fn shrink_to(&mut self, min_capacity: usize) {
482        self.try_shrink_to(min_capacity).unwrap_with_msg()
483    }
484
485    /// Fallible version of [`LeanString::shrink_to()`].
486    ///
487    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
488    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::shrink_to()`].
489    #[inline]
490    pub fn try_shrink_to(&mut self, min_capacity: usize) -> Result<(), ReserveError> {
491        self.0.shrink_to(min_capacity)
492    }
493
494    /// Appends the given [`char`] to the end of the [`LeanString`].
495    ///
496    /// # Panics
497    ///
498    /// Panics if the system is out-of-memory. If you want to handle such a problem manually, use
499    /// [`LeanString::try_push()`].
500    ///
501    /// # Examples
502    ///
503    /// ```
504    /// # use lean_string::LeanString;
505    /// let mut s = LeanString::new();
506    /// s.push('f');
507    /// s.push('o');
508    /// s.push('o');
509    /// assert_eq!("foo", s);
510    /// ```
511    #[inline]
512    pub fn push(&mut self, ch: char) {
513        self.try_push(ch).unwrap_with_msg()
514    }
515
516    /// Fallible version of [`LeanString::push()`].
517    ///
518    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
519    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::push()`].
520    #[inline]
521    pub fn try_push(&mut self, ch: char) -> Result<(), ReserveError> {
522        self.0.push_str(ch.encode_utf8(&mut [0; 4]))
523    }
524
525    /// Removes the last character from the [`LeanString`] and returns it.
526    /// If the [`LeanString`] is empty, `None` is returned.
527    ///
528    /// # Panics
529    ///
530    /// This method does not clone and panics the [`LeanString`] **without all** of following conditions are
531    /// true:
532    ///
533    /// - 32-bit architecture
534    /// - The [`LeanString`] is not unique.
535    /// - The length of the [`LeanString`] is greater than `2^26 - 1`.
536    ///
537    /// If you want to handle such a problem manually, use [`LeanString::try_pop()`].
538    ///
539    /// # Examples
540    ///
541    /// ```
542    /// # use lean_string::LeanString;
543    /// let mut s = LeanString::from("abč");
544    ///
545    /// assert_eq!(s.pop(), Some('č'));
546    /// assert_eq!(s.pop(), Some('b'));
547    /// assert_eq!(s.pop(), Some('a'));
548    ///
549    /// assert_eq!(s.pop(), None);
550    /// ```
551    #[inline]
552    pub fn pop(&mut self) -> Option<char> {
553        self.try_pop().unwrap_with_msg()
554    }
555
556    /// Fallible version of [`LeanString::pop()`].
557    ///
558    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
559    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::pop()`].
560    #[inline]
561    pub fn try_pop(&mut self) -> Result<Option<char>, ReserveError> {
562        self.0.pop()
563    }
564
565    /// Appends a given string slice onto the end of this [`LeanString`].
566    ///
567    /// # Panics
568    ///
569    /// Panics if cloning the [`LeanString`] fails due to the system being out-of-memory. If you
570    /// want to handle such a problem manually, use [`LeanString::try_push_str()`].
571    ///
572    /// # Examples
573    ///
574    /// ```
575    /// # use lean_string::LeanString;
576    /// let mut s = LeanString::from("foo");
577    ///
578    /// s.push_str("bar");
579    ///
580    /// assert_eq!("foobar", s);
581    /// ```
582    #[inline]
583    pub fn push_str(&mut self, string: &str) {
584        self.try_push_str(string).unwrap_with_msg()
585    }
586
587    /// Fallible version of [`LeanString::push_str()`].
588    ///
589    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
590    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::push_str()`].
591    #[inline]
592    pub fn try_push_str(&mut self, string: &str) -> Result<(), ReserveError> {
593        self.0.push_str(string)
594    }
595
596    /// Removes a [`char`] from the [`LeanString`] at a byte position and returns it.
597    ///
598    /// # Panics
599    ///
600    /// Panics if **any** of the following conditions:
601    ///
602    /// 1. `idx` is larger than or equal tothe [`LeanString`]'s length, or if it does not lie on a [`char`]
603    /// 2. The system is out-of-memory when cloning the [`LeanString`].
604    ///
605    /// For 2, if you want to handle such a problem manually, use [`LeanString::try_remove()`].
606    ///
607    /// # Examples
608    ///
609    /// ```
610    /// # use lean_string::LeanString;
611    /// let mut s = LeanString::from("Hello 世界");
612    ///
613    /// assert_eq!(s.remove(6), '世');
614    /// assert_eq!(s.remove(1), 'e');
615    ///
616    /// assert_eq!(s, "Hllo 界");
617    /// ```
618    /// ## Past total length:
619    ///
620    /// ```should_panic
621    /// # use lean_string::LeanString;
622    /// let mut c = LeanString::from("hello there!");
623    /// c.remove(12);
624    /// ```
625    ///
626    /// ## Not on char boundary:
627    ///
628    /// ```should_panic
629    /// # use lean_string::LeanString;
630    /// let mut c = LeanString::from("🦄");
631    /// c.remove(1);
632    /// ```
633    #[inline]
634    pub fn remove(&mut self, idx: usize) -> char {
635        self.try_remove(idx).unwrap_with_msg()
636    }
637
638    /// Fallible version of [`LeanString::remove()`].
639    ///
640    /// This method won't panic if the system is out-of-memory, but return an [`ReserveError`].
641    /// Otherwise it behaves the same as [`LeanString::remove()`].
642    ///
643    /// # Panics
644    ///
645    /// This method still panics if the `idx` is larger than or equal to the [`LeanString`]'s
646    /// length, or if it does not lie on a [`char`] boundary.
647    #[inline]
648    pub fn try_remove(&mut self, idx: usize) -> Result<char, ReserveError> {
649        self.0.remove(idx)
650    }
651
652    /// Retains only the characters specified by the `predicate`.
653    ///
654    /// If the `predicate` returns `true`, the character is kept, otherwise it is removed.
655    ///
656    /// # Panics
657    ///
658    /// Panics if the system is out-of-memory when cloning the [`LeanString`]. If you want to
659    /// handle such a problem manually, use [`LeanString::try_retain()`].
660    ///
661    /// # Examples
662    ///
663    /// ```
664    /// # use lean_string::LeanString;
665    /// let mut s = LeanString::from("äb𝄞d€");
666    ///
667    /// let keep = [false, true, true, false, true];
668    /// let mut iter = keep.iter();
669    /// s.retain(|_| *iter.next().unwrap());
670    ///
671    /// assert_eq!(s, "b𝄞€");
672    /// ```
673    #[inline]
674    pub fn retain(&mut self, predicate: impl FnMut(char) -> bool) {
675        self.try_retain(predicate).unwrap_with_msg()
676    }
677
678    /// Fallible version of [`LeanString::retain()`].
679    ///
680    /// This method won't panic if the system is out-of-memory, but return an [`ReserveError`].
681    #[inline]
682    pub fn try_retain(&mut self, predicate: impl FnMut(char) -> bool) -> Result<(), ReserveError> {
683        self.0.retain(predicate)
684    }
685
686    /// Inserts a character into the [`LeanString`] at a byte position.
687    ///
688    /// # Panics
689    ///
690    /// Panics if **any** of the following conditions:
691    ///
692    /// 1. `idx` is larger than the [`LeanString`]'s length, or if it does not lie on a [`char`]
693    ///    boundary.
694    /// 2. The system is out-of-memory when cloning the [`LeanString`].
695    /// 3. The length of after inserting is greater than `2^56 - 1` on 64-bit architecture, or
696    ///    `2^32 - 1` on 32-bit architecture.
697    ///
698    /// For 2 and 3, if you want to handle such a problem manually, use [`LeanString::try_insert()`].
699    ///
700    /// # Examples
701    ///
702    /// ```
703    /// # use lean_string::LeanString;
704    /// let mut s = LeanString::from("Hello world");
705    ///
706    /// s.insert(11, '!');
707    /// assert_eq!("Hello world!", s);
708    ///
709    /// s.insert(5, ',');
710    /// assert_eq!("Hello, world!", s);
711    /// ```
712    #[inline]
713    pub fn insert(&mut self, idx: usize, ch: char) {
714        self.try_insert(idx, ch).unwrap_with_msg()
715    }
716
717    /// Fallible version of [`LeanString::insert()`].
718    ///
719    /// This method won't panic if the system is out-of-memory, or the `capacity` becomes too large
720    /// by inserting a character, but return an [`ReserveError`]. Otherwise it behaves the same as
721    /// [`LeanString::insert()`].
722    ///
723    /// # Panics
724    ///
725    /// This method still panics if the `idx` is larger than the [`LeanString`]'s length, or if it
726    /// does not lie on a [`char`] boundary.
727    #[inline]
728    pub fn try_insert(&mut self, idx: usize, ch: char) -> Result<(), ReserveError> {
729        self.0.insert_str(idx, ch.encode_utf8(&mut [0; 4]))
730    }
731
732    /// Inserts a string slice into the [`LeanString`] at a byte position.
733    ///
734    /// # Panics
735    ///
736    /// Panics if **any** of the following conditions:
737    ///
738    /// 1. `idx` is larger than the [`LeanString`]'s length, or if it does not lie on a [`char`] boundary.
739    /// 2. The system is out-of-memory when cloning the [`LeanString`].
740    /// 3. The length of after inserting is greater than `2^56 - 1` on 64-bit architecture, or
741    ///    `2^32 - 1` on 32-bit architecture.
742    ///
743    /// For 2 and 3, if you want to handle such a problem manually, use [`LeanString::try_insert_str()`].
744    ///
745    /// # Examples
746    /// ```
747    /// # use lean_string::LeanString;
748    /// let mut s = LeanString::from("bar");
749    /// s.insert_str(0, "foo");
750    /// assert_eq!("foobar", s);
751    /// ```
752    #[inline]
753    pub fn insert_str(&mut self, idx: usize, string: &str) {
754        self.try_insert_str(idx, string).unwrap_with_msg()
755    }
756
757    /// Fallible version of [`LeanString::insert_str()`].
758    ///
759    /// This method won't panic if the system is out-of-memory, or the `capacity` becomes too large
760    /// by inserting a string slice, but return an [`ReserveError`]. Otherwise it behaves the same
761    /// as [`LeanString::insert_str()`].
762    ///
763    /// # Panics
764    ///
765    /// This method still panics if the `idx` is larger than the [`LeanString`]'s length, or if it
766    /// does not lie on a [`char`] boundary.
767    #[inline]
768    pub fn try_insert_str(&mut self, idx: usize, string: &str) -> Result<(), ReserveError> {
769        self.0.insert_str(idx, string)
770    }
771
772    /// Shortens a [`LeanString`] to the specified length.
773    ///
774    /// If `new_len` is greater than or equal to the string's current length, this has no effect.
775    ///
776    /// # Panics
777    ///
778    /// Panics if **any** of the following conditions is met:
779    ///
780    /// 1. `new_len` does not lie on a [`char`] boundary.
781    /// 2. The system is out-of-memory when cloning the [`LeanString`].
782    ///
783    /// For 2, If you want to handle such a problem manually, use [`LeanString::try_truncate()`].
784    ///
785    /// # Examples
786    ///
787    /// ```
788    /// # use lean_string::LeanString;
789    /// let mut s = LeanString::from("hello");
790    /// s.truncate(2);
791    /// assert_eq!(s, "he");
792    ///
793    /// // Truncating to a larger length does nothing:
794    /// s.truncate(10);
795    /// assert_eq!(s, "he");
796    /// ```
797    #[inline]
798    pub fn truncate(&mut self, new_len: usize) {
799        self.try_truncate(new_len).unwrap_with_msg()
800    }
801
802    /// Fallible version of [`LeanString::truncate()`].
803    ///
804    /// This method won't panic if the system is out-of-memory, but return an [`ReserveError`].
805    /// Otherwise it behaves the same as [`LeanString::truncate()`].
806    ///
807    /// # Panics
808    ///
809    /// This method still panics if `new_len` does not lie on a [`char`] boundary.
810    #[inline]
811    pub fn try_truncate(&mut self, new_len: usize) -> Result<(), ReserveError> {
812        self.0.truncate(new_len)
813    }
814
815    /// Reduces the length of the [`LeanString`] to zero.
816    ///
817    /// If the [`LeanString`] is unique, this method will not change the capacity.
818    /// Otherwise, creates a new unique [`LeanString`] without heap allocation.
819    ///
820    /// # Examples
821    ///
822    /// ## unique
823    ///
824    /// ```
825    /// # use lean_string::LeanString;
826    /// let mut s = LeanString::from("This is a example of unique LeanString");
827    /// assert_eq!(s.capacity(), 38);
828    ///
829    /// s.clear();
830    ///
831    /// assert_eq!(s, "");
832    /// assert_eq!(s.capacity(), 38);
833    /// ```
834    ///
835    /// ## not unique
836    ///
837    /// ```
838    /// # use lean_string::LeanString;
839    /// let mut s = LeanString::from("This is a example of not unique LeanString");
840    /// assert_eq!(s.capacity(), 42);
841    ///
842    /// let s2 = s.clone();
843    /// s.clear();
844    ///
845    /// assert_eq!(s, "");
846    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
847    /// ```
848    #[inline]
849    pub fn clear(&mut self) {
850        if self.0.is_unique() {
851            // SAFETY:
852            // - `self` is unique.
853            // - 0 bytes is always valid UTF-8, and initialized.
854            unsafe { self.0.set_len(0) }
855        } else {
856            self.0.replace_inner(Repr::new());
857        }
858    }
859
860    /// Returns whether the [`LeanString`] is heap-allocated.
861    ///
862    /// # Examples
863    ///
864    /// ## inline
865    ///
866    /// ```
867    /// # use lean_string::LeanString;
868    /// let s = LeanString::from("hello");
869    /// assert!(!s.is_heap_allocated());
870    /// ```
871    ///
872    /// ## heap
873    ///
874    /// ```
875    /// # use lean_string::LeanString;
876    /// let s = LeanString::from("More than 2 * size_of::<usize>() bytes is heap-allocated");
877    /// assert!(s.is_heap_allocated());
878    /// ```
879    #[inline]
880    pub fn is_heap_allocated(&self) -> bool {
881        self.0.is_heap_buffer()
882    }
883}
884
885/// A [`Clone`] implementation for [`LeanString`].
886///
887/// The clone operation is performed using a reference counting mechanism, which means:
888/// - The cloned string shares the same underlying data with the original string
889/// - The cloning process is very efficient (O(1) time complexity)
890/// - No memory allocation occurs during cloning
891///
892/// # Examples
893///
894/// ```
895/// # use lean_string::LeanString;
896/// let s1 = LeanString::from("Hello, World!");
897/// let s2 = s1.clone();
898///
899/// assert_eq!(s1, s2);
900/// ```
901impl Clone for LeanString {
902    #[inline]
903    fn clone(&self) -> Self {
904        LeanString(self.0.make_shallow_clone())
905    }
906
907    #[inline]
908    fn clone_from(&mut self, source: &Self) {
909        self.0.replace_inner(source.0.make_shallow_clone());
910    }
911}
912
913/// A [`Drop`] implementation for [`LeanString`].
914///
915/// When the last reference to a [`LeanString`] is dropped:
916/// - If the string is heap-allocated, the heap memory is freed
917/// - The internal state is reset to an empty inline buffer
918///
919/// This ensures no memory leaks occur and all resources are properly cleaned up.
920impl Drop for LeanString {
921    fn drop(&mut self) {
922        self.0.replace_inner(Repr::new());
923    }
924}
925
926// SAFETY: `LeanString` is `repr(transparent)` over `Repr`, and `Repr` works like `Arc`.
927unsafe impl Send for LeanString {}
928unsafe impl Sync for LeanString {}
929
930impl Default for LeanString {
931    #[inline]
932    fn default() -> Self {
933        Self::new()
934    }
935}
936
937impl Deref for LeanString {
938    type Target = str;
939
940    #[inline]
941    fn deref(&self) -> &str {
942        self.as_str()
943    }
944}
945
946impl fmt::Debug for LeanString {
947    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
948        fmt::Debug::fmt(self.as_str(), f)
949    }
950}
951
952impl fmt::Display for LeanString {
953    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
954        fmt::Display::fmt(self.as_str(), f)
955    }
956}
957
958impl AsRef<str> for LeanString {
959    #[inline]
960    fn as_ref(&self) -> &str {
961        self.as_str()
962    }
963}
964
965#[cfg(feature = "std")]
966impl AsRef<OsStr> for LeanString {
967    #[inline]
968    fn as_ref(&self) -> &OsStr {
969        OsStr::new(self.as_str())
970    }
971}
972
973impl AsRef<[u8]> for LeanString {
974    #[inline]
975    fn as_ref(&self) -> &[u8] {
976        self.as_bytes()
977    }
978}
979
980impl Borrow<str> for LeanString {
981    #[inline]
982    fn borrow(&self) -> &str {
983        self.as_str()
984    }
985}
986
987impl Eq for LeanString {}
988
989impl PartialEq for LeanString {
990    #[inline]
991    fn eq(&self, other: &Self) -> bool {
992        self.as_str().eq(other.as_str())
993    }
994}
995
996impl PartialEq<str> for LeanString {
997    #[inline]
998    fn eq(&self, other: &str) -> bool {
999        self.as_str().eq(other)
1000    }
1001}
1002
1003impl PartialEq<LeanString> for str {
1004    #[inline]
1005    fn eq(&self, other: &LeanString) -> bool {
1006        self.eq(other.as_str())
1007    }
1008}
1009
1010impl PartialEq<&str> for LeanString {
1011    #[inline]
1012    fn eq(&self, other: &&str) -> bool {
1013        self.as_str().eq(*other)
1014    }
1015}
1016
1017impl PartialEq<LeanString> for &str {
1018    #[inline]
1019    fn eq(&self, other: &LeanString) -> bool {
1020        (*self).eq(other.as_str())
1021    }
1022}
1023
1024impl PartialEq<String> for LeanString {
1025    #[inline]
1026    fn eq(&self, other: &String) -> bool {
1027        self.as_str().eq(other.as_str())
1028    }
1029}
1030
1031impl PartialEq<LeanString> for String {
1032    #[inline]
1033    fn eq(&self, other: &LeanString) -> bool {
1034        self.as_str().eq(other.as_str())
1035    }
1036}
1037
1038impl PartialEq<Cow<'_, str>> for LeanString {
1039    #[inline]
1040    fn eq(&self, other: &Cow<'_, str>) -> bool {
1041        self.as_str().eq(other.as_ref())
1042    }
1043}
1044
1045impl PartialEq<LeanString> for Cow<'_, str> {
1046    #[inline]
1047    fn eq(&self, other: &LeanString) -> bool {
1048        self.as_ref().eq(other.as_str())
1049    }
1050}
1051
1052impl Ord for LeanString {
1053    #[inline]
1054    fn cmp(&self, other: &Self) -> cmp::Ordering {
1055        self.as_str().cmp(other.as_str())
1056    }
1057}
1058
1059impl PartialOrd for LeanString {
1060    #[inline]
1061    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1062        Some(self.cmp(other))
1063    }
1064}
1065
1066impl Hash for LeanString {
1067    #[inline]
1068    fn hash<H: Hasher>(&self, state: &mut H) {
1069        self.as_str().hash(state)
1070    }
1071}
1072
1073impl From<char> for LeanString {
1074    #[inline]
1075    #[track_caller]
1076    fn from(value: char) -> Self {
1077        LeanString(Repr::from_char(value))
1078    }
1079}
1080
1081impl From<&str> for LeanString {
1082    #[inline]
1083    #[track_caller]
1084    fn from(value: &str) -> Self {
1085        LeanString(Repr::from_str(value).unwrap_with_msg())
1086    }
1087}
1088
1089impl From<String> for LeanString {
1090    #[inline]
1091    #[track_caller]
1092    fn from(value: String) -> Self {
1093        LeanString(Repr::from_str(&value).unwrap_with_msg())
1094    }
1095}
1096
1097impl From<&String> for LeanString {
1098    #[inline]
1099    #[track_caller]
1100    fn from(value: &String) -> Self {
1101        LeanString(Repr::from_str(value).unwrap_with_msg())
1102    }
1103}
1104
1105impl From<Cow<'_, str>> for LeanString {
1106    fn from(cow: Cow<str>) -> Self {
1107        match cow {
1108            Cow::Borrowed(s) => s.into(),
1109            Cow::Owned(s) => s.into(),
1110        }
1111    }
1112}
1113
1114impl From<Box<str>> for LeanString {
1115    #[inline]
1116    #[track_caller]
1117    fn from(value: Box<str>) -> Self {
1118        LeanString(Repr::from_str(&value).unwrap_with_msg())
1119    }
1120}
1121
1122impl From<&LeanString> for LeanString {
1123    #[inline]
1124    fn from(value: &LeanString) -> Self {
1125        value.clone()
1126    }
1127}
1128
1129impl From<LeanString> for String {
1130    #[inline]
1131    fn from(value: LeanString) -> Self {
1132        value.as_str().into()
1133    }
1134}
1135
1136impl From<&LeanString> for String {
1137    #[inline]
1138    fn from(value: &LeanString) -> Self {
1139        value.as_str().into()
1140    }
1141}
1142
1143impl FromStr for LeanString {
1144    type Err = ReserveError;
1145
1146    #[inline]
1147    fn from_str(s: &str) -> Result<Self, Self::Err> {
1148        Repr::from_str(s).map(Self)
1149    }
1150}
1151
1152impl FromIterator<char> for LeanString {
1153    fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
1154        let iter = iter.into_iter();
1155
1156        let (lower_bound, _) = iter.size_hint();
1157        let mut repr = match Repr::with_capacity(lower_bound) {
1158            Ok(buf) => buf,
1159            Err(_) => Repr::new(), // Ignore the error and hope that the lower_bound is incorrect.
1160        };
1161
1162        for ch in iter {
1163            repr.push_str(ch.encode_utf8(&mut [0; 4])).unwrap_with_msg();
1164        }
1165        LeanString(repr)
1166    }
1167}
1168
1169impl<'a> FromIterator<&'a char> for LeanString {
1170    fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
1171        iter.into_iter().copied().collect()
1172    }
1173}
1174
1175impl<'a> FromIterator<&'a str> for LeanString {
1176    fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Self {
1177        let mut buf = LeanString::new();
1178        buf.extend(iter);
1179        buf
1180    }
1181}
1182
1183impl FromIterator<Box<str>> for LeanString {
1184    fn from_iter<I: IntoIterator<Item = Box<str>>>(iter: I) -> Self {
1185        let mut buf = LeanString::new();
1186        buf.extend(iter);
1187        buf
1188    }
1189}
1190
1191impl<'a> FromIterator<Cow<'a, str>> for LeanString {
1192    fn from_iter<I: IntoIterator<Item = Cow<'a, str>>>(iter: I) -> Self {
1193        let mut buf = LeanString::new();
1194        buf.extend(iter);
1195        buf
1196    }
1197}
1198
1199impl FromIterator<String> for LeanString {
1200    fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
1201        let mut buf = LeanString::new();
1202        buf.extend(iter);
1203        buf
1204    }
1205}
1206
1207impl FromIterator<LeanString> for LeanString {
1208    fn from_iter<T: IntoIterator<Item = LeanString>>(iter: T) -> Self {
1209        let mut buf = LeanString::new();
1210        buf.extend(iter);
1211        buf
1212    }
1213}
1214
1215impl Extend<char> for LeanString {
1216    fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
1217        let iter = iter.into_iter();
1218
1219        let (lower_bound, _) = iter.size_hint();
1220        // Ignore the error and hope that the lower_bound is incorrect.
1221        let _ = self.try_reserve(lower_bound);
1222
1223        for ch in iter {
1224            self.push(ch);
1225        }
1226    }
1227}
1228
1229impl<'a> Extend<&'a char> for LeanString {
1230    fn extend<T: IntoIterator<Item = &'a char>>(&mut self, iter: T) {
1231        self.extend(iter.into_iter().copied());
1232    }
1233}
1234
1235impl<'a> Extend<&'a str> for LeanString {
1236    fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
1237        iter.into_iter().for_each(|s| self.push_str(s));
1238    }
1239}
1240
1241impl Extend<Box<str>> for LeanString {
1242    fn extend<T: IntoIterator<Item = Box<str>>>(&mut self, iter: T) {
1243        iter.into_iter().for_each(move |s| self.push_str(&s));
1244    }
1245}
1246
1247impl<'a> Extend<Cow<'a, str>> for LeanString {
1248    fn extend<T: IntoIterator<Item = Cow<'a, str>>>(&mut self, iter: T) {
1249        iter.into_iter().for_each(move |s| self.push_str(&s));
1250    }
1251}
1252
1253impl Extend<String> for LeanString {
1254    fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
1255        iter.into_iter().for_each(move |s| self.push_str(&s));
1256    }
1257}
1258
1259impl Extend<LeanString> for LeanString {
1260    fn extend<T: IntoIterator<Item = LeanString>>(&mut self, iter: T) {
1261        for s in iter {
1262            self.push_str(&s);
1263        }
1264    }
1265}
1266
1267impl Extend<LeanString> for String {
1268    fn extend<T: IntoIterator<Item = LeanString>>(&mut self, iter: T) {
1269        for s in iter {
1270            self.push_str(&s);
1271        }
1272    }
1273}
1274
1275impl fmt::Write for LeanString {
1276    #[inline]
1277    fn write_str(&mut self, s: &str) -> fmt::Result {
1278        self.push_str(s);
1279        Ok(())
1280    }
1281}
1282
1283impl Add<&str> for LeanString {
1284    type Output = Self;
1285
1286    #[inline]
1287    fn add(mut self, rhs: &str) -> Self::Output {
1288        self.push_str(rhs);
1289        self
1290    }
1291}
1292
1293impl AddAssign<&str> for LeanString {
1294    #[inline]
1295    fn add_assign(&mut self, rhs: &str) {
1296        self.push_str(rhs);
1297    }
1298}
1299
1300trait UnwrapWithMsg {
1301    type T;
1302    fn unwrap_with_msg(self) -> Self::T;
1303}
1304
1305impl<T, E: fmt::Display> UnwrapWithMsg for Result<T, E> {
1306    type T = T;
1307    #[inline(always)]
1308    #[track_caller]
1309    fn unwrap_with_msg(self) -> T {
1310        #[inline(never)]
1311        #[cold]
1312        #[track_caller]
1313        fn do_panic_with_msg<E: fmt::Display>(error: E) -> ! {
1314            panic!("{error}")
1315        }
1316
1317        match self {
1318            Ok(value) => value,
1319            Err(err) => do_panic_with_msg(err),
1320        }
1321    }
1322}