zalgo_codec_common/zalgo_string/
mod.rs

1//! Contains the implementation of [`ZalgoString`] as well as related iterators.
2//!
3//! A `ZalgoString` contains a grapheme cluster that was obtained from [`zalgo_encode`].
4//! It allows for iteration over its characters and bytes in both encoded and decoded form.
5//! It can be decoded in-place and the encoded information in other ZalgoStrings can be pushed
6//! onto it.
7
8mod iterators;
9
10use crate::{decode_byte_pair, fmt, zalgo_encode, EncodeError};
11pub use iterators::{DecodedBytes, DecodedChars};
12
13use core::{ops::Index, slice::SliceIndex};
14
15use alloc::{borrow::Cow, string::String, vec::Vec};
16
17/// A [`String`] that has been encoded with [`zalgo_encode`].
18/// This struct can be decoded in-place and also allows iteration over its characters and bytes, both in
19/// decoded and encoded form.
20#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[cfg_attr(
23    feature = "rkyv",
24    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
25)]
26pub struct ZalgoString(String);
27
28/// Allocates a `String` that contains only the character "E" and no encoded content.
29impl Default for ZalgoString {
30    fn default() -> Self {
31        Self(String::from('E'))
32    }
33}
34
35impl ZalgoString {
36    /// Encodes the given string slice with [`zalgo_encode`] and stores the result in a new allocation.
37    ///
38    /// # Errors
39    ///
40    /// Returns an error if the input string contains bytes that don't correspond to printable
41    /// ASCII characters or newlines.
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
47    /// assert_eq!(ZalgoString::new("Zalgo")?, "É̺͇͌͏");
48    /// # Ok::<(), EncodeError>(())
49    /// ```
50    /// Can only encode printable ASCII and newlines:
51    /// ```
52    /// # use zalgo_codec_common::ZalgoString;
53    /// assert!(ZalgoString::new("❤️").is_err());
54    /// assert!(ZalgoString::new("\r").is_err());
55    /// ```
56    #[must_use = "this associated method returns a new `ZalgoString` and does not modify the input"]
57    pub fn new(s: &str) -> Result<Self, EncodeError> {
58        zalgo_encode(s).map(Self)
59    }
60
61    /// Creates a new `ZalgoString` with at least the specified capacity.
62    ///
63    /// A ZalgoString always has an allocated buffer with an "E" in it,
64    /// so the capacity can not be zero.
65    ///
66    /// If you want the ZalgoString to have capacity for x encoded characters
67    /// you must reserve a capacity of 2x + 1.
68    ///
69    /// # Example
70    ///
71    /// ```
72    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
73    /// use core::num::NonZeroUsize;
74    ///
75    /// // Reserve capacity for two encoded characters
76    /// let capacity = NonZeroUsize::new(2*2 + 1).unwrap();
77    /// let mut zs = ZalgoString::with_capacity(capacity);
78    ///
79    /// // This ZalgoString would decode into an empty string
80    /// assert_eq!(zs.decoded_len(), 0);
81    ///
82    /// // This allocates,
83    /// let zs2 = ZalgoString::new("Hi")?;
84    ///
85    /// // but this does not reallocate `zs`
86    /// let cap = zs.capacity();
87    /// zs.push_zalgo_str(&zs2);
88    /// assert_eq!(zs.capacity(), cap);
89    ///
90    /// # Ok::<(), EncodeError>(())
91    /// ```
92    #[inline]
93    #[must_use = "this associated method return a new `ZalgoString` and does not modify the input"]
94    pub fn with_capacity(capacity: core::num::NonZeroUsize) -> Self {
95        let mut s = String::with_capacity(capacity.get());
96        s.push('E');
97        Self(s)
98    }
99
100    // region: character access methods
101
102    /// Returns the *encoded* contents of `self` as a string slice.
103    ///
104    /// # Example
105    ///
106    /// Basic usage
107    /// ```
108    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
109    /// let zs = ZalgoString::new("Oh boy!")?;
110    /// assert_eq!(zs.as_str(), "È̯͈͂͏͙́");
111    /// # Ok::<(), EncodeError>(())
112    /// ```
113    /// Note that `ZalgoString` implements [`PartialEq`] with common string types,
114    /// so the comparison in the above example could also be done directly
115    /// ```
116    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
117    /// # let zs = ZalgoString::new("Oh boy!")?;
118    /// assert_eq!(zs, "È̯͈͂͏͙́");
119    /// # Ok::<(), EncodeError>(())
120    /// ```
121    #[inline]
122    #[must_use = "the method returns a reference and does not modify `self`"]
123    pub fn as_str(&self) -> &str {
124        &self.0
125    }
126
127    /// Returns a subslice of `self`.
128    ///
129    /// Same as [`str::get`].
130    ///
131    /// This is the non-panicking alternative to indexing the `ZalgoString`. Returns [`None`] whenever
132    /// the equivalent indexing operation would panic.
133    ///
134    /// # Example
135    ///
136    /// ```
137    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
138    /// let zs = ZalgoString::new("Zalgo")?;
139    /// assert_eq!(zs.get(0..3), Some("E\u{33a}"));
140    ///
141    /// // indices not on UTF-8 sequence boundaries
142    /// assert!(zs.get(0..4).is_none());
143    ///
144    /// // out of bounds
145    /// assert!(zs.get(..42).is_none());
146    /// # Ok::<(), EncodeError>(())
147    /// ```
148    #[inline]
149    pub fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<str>>::Output>
150    where
151        I: SliceIndex<str>,
152    {
153        self.0.get(index)
154    }
155
156    /// Returns an unchecked subslice of `self`.
157    ///
158    /// This is the unchecked alternative to indexing a `ZalgoString`.
159    ///
160    /// # Safety
161    ///
162    /// This function has the same safety requirements as [`str::get_unchecked`]:
163    /// - The starting index must not exceed the ending index;
164    /// - Indexes must be within bounds of the original slice;
165    /// - Indexes must lie on UTF-8 sequence boundaries.
166    ///
167    /// # Example
168    ///
169    /// ```
170    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
171    /// let zs = ZalgoString::new("Zalgo")?;
172    /// unsafe {
173    ///     assert_eq!(zs.get_unchecked(..3), "E\u{33a}");
174    /// }
175    /// # Ok::<(), EncodeError>(())
176    /// ```
177    #[inline]
178    pub unsafe fn get_unchecked<I>(&self, index: I) -> &<I as SliceIndex<str>>::Output
179    where
180        I: SliceIndex<str>,
181    {
182        self.0.get_unchecked(index)
183    }
184
185    /// Returns an iterator over the encoded characters of the `ZalgoString`.
186    ///
187    /// The first character is an "E", the others are unicode combining characters.
188    ///
189    /// # Example
190    ///
191    /// Iterate through the encoded [`char`]s:
192    /// ```
193    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
194    /// let zs = ZalgoString::new("42")?;
195    /// let mut chars = zs.chars();
196    /// assert_eq!(chars.next(), Some('E'));
197    /// assert_eq!(chars.next(), Some('\u{314}'));
198    /// # Ok::<(), EncodeError>(())
199    /// ```
200    #[inline]
201    pub fn chars(&self) -> core::str::Chars<'_> {
202        self.0.chars()
203    }
204
205    /// Returns an iterator over the encoded characters of the `ZalgoString` and their positions.
206    ///
207    /// # Example
208    ///
209    /// Combining characters lie deep in the dark depths of Unicode,
210    /// and may not match with your intuition of what a character is.
211    /// ```
212    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
213    /// let zs = ZalgoString::new("Zalgo")?;
214    /// let mut ci = zs.char_indices();
215    /// assert_eq!(ci.next(), Some((0, 'E')));
216    /// assert_eq!(ci.next(), Some((1,'\u{33a}')));
217    /// // Note the 3 here, the combining characters take up two bytes.
218    /// assert_eq!(ci.next(), Some((3, '\u{341}')));
219    /// // The final character begins at position 9
220    /// assert_eq!(ci.next_back(), Some((9, '\u{34f}')));
221    /// // even though the length in bytes is 11
222    /// assert_eq!(zs.len(), 11);
223    /// # Ok::<(), EncodeError>(())
224    /// ```
225    #[inline]
226    pub fn char_indices(&self) -> core::str::CharIndices<'_> {
227        self.0.char_indices()
228    }
229
230    /// Returns an iterator over the decoded characters of the `ZalgoString`.
231    ///
232    /// These characters are guaranteed to be valid ASCII.
233    ///
234    /// # Example
235    ///
236    /// ```
237    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
238    /// let zs = ZalgoString::new("Zlgoa")?;
239    /// let mut decoded_chars = zs.decoded_chars();
240    /// assert_eq!(decoded_chars.next(), Some('Z'));
241    /// assert_eq!(decoded_chars.next_back(), Some('a'));
242    /// assert_eq!(decoded_chars.next(), Some('l'));
243    /// assert_eq!(decoded_chars.next(), Some('g'));
244    /// assert_eq!(decoded_chars.next_back(), Some('o'));
245    /// assert_eq!(decoded_chars.next(), None);
246    /// assert_eq!(decoded_chars.next_back(), None);
247    /// # Ok::<(), EncodeError>(())
248    /// ```
249    #[inline]
250    pub fn decoded_chars(&self) -> DecodedChars<'_> {
251        DecodedChars::new(self)
252    }
253
254    /// Converts `self` into a `String`.
255    ///
256    /// This simply returns the underlying `String` without any cloning or decoding.
257    ///
258    /// # Example
259    ///
260    /// Basic usage
261    /// ```
262    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
263    /// let zs = ZalgoString::new("Zalgo\n He comes!")?;
264    /// assert_eq!(zs.into_string(), "É̺͇͌͏̨ͯ̀̀̓ͅ͏͍͓́ͅ");
265    /// # Ok::<(), EncodeError>(())
266    /// ```
267    #[inline]
268    #[must_use = "`self` will be dropped if the result is not used"]
269    pub fn into_string(self) -> String {
270        self.0
271    }
272
273    /// Decodes `self` into a `String` in-place.
274    ///
275    /// This method has no effect on the allocated capacity.
276    ///
277    /// # Example
278    ///
279    /// Basic usage
280    /// ```
281    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
282    /// let s = "Zalgo";
283    /// let zs = ZalgoString::new(s)?;
284    /// assert_eq!(s, zs.into_decoded_string());
285    /// # Ok::<(), EncodeError>(())
286    /// ```
287    #[must_use = "`self` will be dropped if the result is not used"]
288    pub fn into_decoded_string(self) -> String {
289        // Safety: we know that the starting string was encoded from valid ASCII to begin with
290        // so every decoded byte is a valid utf-8 character.
291        unsafe { String::from_utf8_unchecked(self.into_decoded_bytes()) }
292    }
293
294    // endregion: character access methods
295
296    // region: byte access methods
297
298    /// Returns the encoded contents of `self` as a byte slice.
299    ///
300    /// The first byte is always 69, after that the bytes no longer correspond to ASCII characters.
301    ///
302    /// # Example
303    ///
304    /// Basic usage
305    /// ```
306    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
307    /// let zs = ZalgoString::new("Zalgo")?;
308    /// let bytes = zs.as_bytes();
309    /// assert_eq!(bytes[0], 69);
310    /// assert_eq!(&bytes[1..5], &[204, 186, 205, 129]);
311    /// # Ok::<(), EncodeError>(())
312    /// ```
313    #[inline]
314    #[must_use = "the method returns a reference and does not modify `self`"]
315    pub fn as_bytes(&self) -> &[u8] {
316        self.0.as_bytes()
317    }
318
319    /// Returns an iterator over the encoded bytes of the `ZalgoString`.
320    ///
321    /// Since a `ZalgoString` always begins with an "E", the first byte is always 69.
322    /// After that the bytes no longer correspond to ASCII values.
323    ///
324    /// # Example
325    ///
326    /// Basic usage
327    /// ```
328    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
329    /// let zs = ZalgoString::new("Bytes")?;
330    /// let mut bytes = zs.bytes();
331    /// assert_eq!(bytes.next(), Some(69));
332    /// assert_eq!(bytes.nth(5), Some(148));
333    /// # Ok::<(), EncodeError>(())
334    /// ```
335    #[inline]
336    pub fn bytes(&self) -> core::str::Bytes<'_> {
337        self.0.bytes()
338    }
339
340    /// Returns an iterator over the decoded bytes of the `ZalgoString`.
341    ///
342    /// These bytes are guaranteed to represent valid ASCII.
343    ///
344    /// # Example
345    ///
346    /// ```
347    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
348    /// let zs = ZalgoString::new("Zalgo")?;
349    /// let mut decoded_bytes = zs.decoded_bytes();
350    /// assert_eq!(decoded_bytes.next(), Some(90));
351    /// assert_eq!(decoded_bytes.next_back(), Some(111));
352    /// assert_eq!(decoded_bytes.collect::<Vec<u8>>(), vec![97, 108, 103]);
353    /// # Ok::<(), EncodeError>(())
354    /// ```
355    #[inline]
356    pub fn decoded_bytes(&self) -> DecodedBytes<'_> {
357        DecodedBytes::new(self)
358    }
359
360    /// Converts `self` into a byte vector.
361    ///
362    /// This simply returns the underlying buffer without any cloning or decoding.
363    ///
364    /// # Example
365    ///
366    /// Basic usage
367    /// ```
368    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
369    /// let zs = ZalgoString::new("Zalgo")?;
370    /// assert_eq!(zs.into_bytes(), vec![69, 204, 186, 205, 129, 205, 140, 205, 135, 205, 143]);
371    /// # Ok::<(), EncodeError>(())
372    /// ```
373    #[inline]
374    #[must_use = "`self` will be dropped if the result is not used"]
375    pub fn into_bytes(self) -> Vec<u8> {
376        self.0.into_bytes()
377    }
378
379    /// Decodes `self` into a byte vector in-place.
380    ///
381    /// This method has no effect on the allocated capacity.
382    ///
383    /// # Example
384    ///
385    /// Basic usage
386    /// ```
387    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
388    /// let zs = ZalgoString::new("Zalgo")?;
389    /// assert_eq!(b"Zalgo".to_vec(), zs.into_decoded_bytes());
390    /// # Ok::<(), EncodeError>(())
391    /// ```
392    #[must_use = "`self` will be dropped if the result is not used"]
393    pub fn into_decoded_bytes(self) -> Vec<u8> {
394        let mut w = 0;
395        let mut bytes = self.into_bytes();
396        for r in (1..bytes.len()).step_by(2) {
397            bytes[w] = decode_byte_pair(bytes[r], bytes[r + 1]);
398            w += 1;
399        }
400        bytes.truncate(w);
401        bytes
402    }
403
404    // endregion: byte access methods
405
406    // region: metadata methods
407
408    /// Returns the length of `self` in bytes.
409    ///
410    /// This length is twice the length of the original `String` plus one.
411    ///
412    /// # Example
413    ///
414    /// Basic usage
415    /// ```
416    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
417    /// let zs = ZalgoString::new("Z")?;
418    /// assert_eq!(zs.len(), 3);
419    /// # Ok::<(), EncodeError>(())
420    /// ```
421    // Since the length is never empty it makes no sense to have an is_empty function.
422    // The decoded length can be empty though, so `decoded_is_empty` is provided instead.
423    #[inline]
424    #[allow(clippy::len_without_is_empty)]
425    #[must_use = "the method returns a new value and does not modify `self`"]
426    pub fn len(&self) -> usize {
427        self.0.len()
428    }
429
430    /// Returns the capacity of the underlying encoded string in bytes.
431    ///
432    /// The `ZalgoString` is preallocated to the needed capacity of twice the length
433    /// of the original unencoded `String` plus one.
434    /// However, this size is not guaranteed since the allocator can choose to allocate more space.
435    #[inline]
436    #[must_use = "the method returns a new value and does not modify `self`"]
437    pub fn capacity(&self) -> usize {
438        self.0.capacity()
439    }
440
441    /// Returns the length of the `ZalgoString` in bytes if it were to be decoded.  
442    ///
443    /// This is computed without any decoding.
444    ///
445    /// # Example
446    ///
447    /// Basic usage
448    /// ```
449    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
450    /// let s = "Zalgo, He comes!";
451    /// let zs = ZalgoString::new(s)?;
452    /// assert_eq!(s.len(), zs.decoded_len());
453    /// # Ok::<(), EncodeError>(())
454    /// ```
455    #[inline]
456    #[must_use = "the method returns a new value and does not modify `self`"]
457    pub fn decoded_len(&self) -> usize {
458        (self.len() - 1) / 2
459    }
460
461    /// Returns whether the string would be empty if decoded.
462    ///
463    /// # Example
464    ///
465    /// Basic usage
466    /// ```
467    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
468    /// let zs = ZalgoString::new("")?;
469    /// assert!(zs.decoded_is_empty());
470    /// let zs = ZalgoString::new("Blargh")?;
471    /// assert!(!zs.decoded_is_empty());
472    /// # Ok::<(), EncodeError>(())
473    /// ```
474    #[inline]
475    #[must_use = "the method returns a new value and does not modify `self`"]
476    pub fn decoded_is_empty(&self) -> bool {
477        self.decoded_len() == 0
478    }
479
480    // endregion: metadata methods
481
482    /// Returns a string slice of just the combining characters of the `ZalgoString` without the inital 'E'.
483    ///
484    /// Note that [`zalgo_decode`](crate::zalgo_decode) assumes that the initial 'E' is present,
485    /// and can not decode the result of this method.
486    ///
487    /// # Example
488    ///
489    /// ```
490    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
491    /// let zs = ZalgoString::new("Hi")?;
492    /// assert_eq!(zs.as_combining_chars(), "\u{328}\u{349}");
493    /// # Ok::<(), EncodeError>(())
494    /// ```
495    #[inline]
496    #[must_use = "the method returns a new value and does not modify `self`"]
497    pub fn as_combining_chars(&self) -> &str {
498        self.0.split_at(1).1
499    }
500
501    /// Converts `self` into a String that contains only the combining characters of the grapheme cluster.
502    ///
503    /// This is an `O(n)` operation since after it has removed the initial "E" it needs to copy every byte
504    /// of the string down one index.
505    ///
506    /// It is the same as calling [`ZalgoString::into_string()`] followed by [`String::remove(0)`](String::remove).
507    ///
508    /// Just like [`as_combining_chars`](ZalgoString::as_combining_chars) the result of this method can not
509    /// be decoded by [`zalgo_decode`](crate::zalgo_decode).
510    ///
511    /// # Example
512    ///
513    /// ```
514    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
515    /// let zs = ZalgoString::new("Hi")?;
516    /// let s = zs.into_combining_chars();
517    /// assert_eq!(s, "\u{328}\u{349}");
518    /// # Ok::<(), EncodeError>(())
519    /// ```
520    #[inline]
521    #[must_use = "`self` will be dropped if the result is not used"]
522    pub fn into_combining_chars(mut self) -> String {
523        self.0.remove(0);
524        self.0
525    }
526
527    /// Appends the combining characters of a different `ZalgoString` to the end of `self`.
528    ///
529    /// # Example
530    ///
531    /// ```
532    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
533    /// let (s1, s2) = ("Zalgo", ", He comes!");
534    ///
535    /// let mut zs1 = ZalgoString::new(s1)?;
536    /// let zs2 = ZalgoString::new(s2)?;
537    ///
538    /// zs1.push_zalgo_str(&zs2);
539    ///
540    /// assert_eq!(zs1.into_decoded_string(), format!("{s1}{s2}"));
541    /// # Ok::<(), EncodeError>(())
542    /// ```
543    #[inline]
544    pub fn push_zalgo_str(&mut self, zalgo_string: &Self) {
545        self.0.push_str(zalgo_string.as_combining_chars());
546    }
547
548    /// Encodes the given string and pushes it onto `self`.
549    ///
550    /// This method encodes the input string into an intermediate allocation and then appends
551    /// the combining characters of the result to the end of `self`. The append step can
552    /// also reallocate if the capacity is not large enough.
553    ///
554    /// See [`push_zalgo_str`](ZalgoString::push_zalgo_str) for a method that does not hide the
555    /// intermediate allocation.
556    ///
557    /// # Errors
558    ///
559    /// Returns an error if the given string contains a character that's not a printable ASCII
560    /// or newline character.
561    ///
562    /// # Example
563    ///
564    /// ```
565    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
566    /// let (s1, s2) = ("Zalgo", ", He comes!");
567    ///
568    /// let mut zs = ZalgoString::new(s1)?;
569    ///
570    /// zs.encode_and_push_str(s2)?;
571    ///
572    /// assert_eq!(zs.into_decoded_string(), format!("{s1}{s2}"));
573    /// # Ok::<(), EncodeError>(())
574    /// ```
575    pub fn encode_and_push_str(&mut self, string: &str) -> Result<(), EncodeError> {
576        self.push_zalgo_str(&ZalgoString::new(string)?);
577        Ok(())
578    }
579
580    // region: capacity manipulation methods
581
582    /// Reserves capacity for at least `additional` bytes more than the current length.
583    ///
584    /// Same as [`String::reserve`].
585    ///
586    /// The allocator may reserve more space to speculatively avoid frequent allocations.
587    /// After calling reserve, capacity will be greater than or equal to `self.len() + additional`.  
588    ///
589    /// Does nothing if the capacity is already sufficient.
590    ///
591    /// Keep in mind that an encoded ASCII character takes up two bytes, and that a `ZalgoString`
592    /// always begins with an unencoded "E" which means that the total length in bytes is always an odd number.
593    ///
594    /// # Example
595    ///
596    /// ```
597    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
598    /// let mut zs = ZalgoString::new("Zalgo")?;
599    /// let c = zs.capacity();
600    /// zs.reserve(5);
601    /// assert!(zs.capacity() >= c + 5);
602    /// # Ok::<(), EncodeError>(())
603    /// ```
604    #[inline]
605    pub fn reserve(&mut self, additional: usize) {
606        self.0.reserve(additional)
607    }
608
609    /// Reserves capacity for exactly `additional` bytes more than the current length.
610    ///
611    /// Same as [`String::reserve_exact`].
612    ///
613    /// Unlike [`reserve`](ZalgoString::reserve), this will not deliberately over-allocate
614    /// to speculatively avoid frequent allocations.
615    /// After calling `reserve_exact`, capacity will be greater than or equal to `self.len() + additional`.
616    ///
617    /// Does nothing if the capacity is already sufficient.
618    ///
619    /// Keep in mind that an encoded ASCII character takes up two bytes, and that a `ZalgoString`
620    /// always begins with an unencoded "E" which means that the total length in bytes is always an odd number.
621    ///
622    /// # Example
623    ///
624    /// ```
625    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
626    /// let mut zs = ZalgoString::new("Zalgo")?;
627    /// let c = zs.capacity();
628    /// zs.reserve_exact(5);
629    /// assert!(zs.capacity() >= c + 5);
630    /// # Ok::<(), EncodeError>(())
631    /// ```
632    #[inline]
633    pub fn reserve_exact(&mut self, additional: usize) {
634        self.0.reserve_exact(additional)
635    }
636
637    // endregion: capacity manipulation methods
638
639    // region: length manipulation methods
640
641    /// Shortens the `ZalgoString` to the specified length.
642    ///
643    /// A `ZalgoString` always takes up an odd number of bytes as the first "E" takes up one,
644    /// and all subsequent characters take up two.
645    ///
646    /// If `new_len` is larger than its current length, this has no effect.
647    ///
648    /// This method has no effect of the allocated capacity.
649    ///
650    /// # Panics
651    ///
652    /// Panics if `new_len` is even.
653    ///
654    /// # Examples
655    ///
656    /// ```
657    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
658    /// let mut zs = ZalgoString::new("Zalgo")?;
659    /// zs.truncate(5);
660    /// assert_eq!(zs, "E\u{33a}\u{341}");
661    /// assert_eq!(zs.into_decoded_string(), "Za");
662    /// # Ok::<(), EncodeError>(())
663    /// ```
664    /// Panics if `new_len` is even:
665    /// ```should_panic
666    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
667    /// let mut zs = ZalgoString::new("Zalgo")?;
668    /// zs.truncate(0);
669    /// # Ok::<(), EncodeError>(())
670    /// ```
671    #[inline]
672    pub fn truncate(&mut self, new_len: usize) {
673        if new_len <= self.len() {
674            assert_eq!(new_len % 2, 1, "the new length must be odd");
675            self.0.truncate(new_len)
676        }
677    }
678
679    /// Truncates this `ZalgoString`, removing all contents except the initial "E".
680    ///
681    /// This means the ZalgoString will have a length of one, but it does not affect its capacity.
682    ///
683    /// # Example
684    ///
685    /// ```
686    /// # use zalgo_codec_common::{EncodeError, ZalgoString};
687    /// let mut zs = ZalgoString::new("Zalgo")?;
688    /// zs.clear();
689    /// assert_eq!(zs, "E");
690    /// assert!(zs.decoded_is_empty());
691    /// # Ok::<(), EncodeError>(())
692    /// ```
693    pub fn clear(&mut self) {
694        self.truncate(1)
695    }
696
697    // endregion: length manipulation methods
698}
699
700// region: Addition impls
701
702/// Implements the `+` operator for concaternating two `ZalgoString`s.
703/// Memorywise it works the same as the `Add` implementation for the normal
704/// `String` type: it consumes the lefthand side, extends its buffer, and
705/// copies the combining characters of the right hand side into it.
706impl core::ops::Add<&ZalgoString> for ZalgoString {
707    type Output = ZalgoString;
708    #[inline]
709    fn add(mut self, rhs: &Self) -> Self::Output {
710        self.push_zalgo_str(rhs);
711        self
712    }
713}
714
715/// Implements the `+=` operator for appending to a `ZalgoString`.
716///
717/// This just calls [`push_zalgo_str`](ZalgoString::push_zalgo_str).
718impl core::ops::AddAssign<&ZalgoString> for ZalgoString {
719    #[inline]
720    fn add_assign(&mut self, rhs: &ZalgoString) {
721        self.push_zalgo_str(rhs);
722    }
723}
724
725// endregion: Addition impls
726
727// region: PartialEq impls
728
729macro_rules! impl_partial_eq {
730    ($($rhs:ty),+) => {
731        $(
732            impl PartialEq<$rhs> for ZalgoString {
733                #[inline]
734                fn eq(&self, other: &$rhs) -> bool {
735                    &self.0 == other
736                }
737            }
738
739            impl PartialEq<ZalgoString> for $rhs {
740                #[inline]
741                fn eq(&self, other: &ZalgoString) -> bool {
742                    self == &other.0
743                }
744            }
745        )+
746    };
747}
748impl_partial_eq! {String, &str, str, Cow<'_, str>}
749
750// endregion: PartialEq impls
751
752/// Displays the encoded form of the `ZalgoString`.
753impl fmt::Display for ZalgoString {
754    #[inline]
755    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
756        write!(f, "{}", self.0)
757    }
758}
759
760impl<I: SliceIndex<str>> Index<I> for ZalgoString {
761    type Output = I::Output;
762    #[inline]
763    fn index(&self, index: I) -> &Self::Output {
764        self.0.as_str().index(index)
765    }
766}
767
768#[cfg(test)]
769mod test {
770    use super::*;
771    use alloc::{
772        format,
773        string::{String, ToString},
774    };
775
776    #[test]
777    fn check_into_decoded_string() {
778        let s = "Zalgo\n He comes!";
779        let zs: ZalgoString = ZalgoString::new(s).unwrap();
780        assert_eq!(zs.into_decoded_string(), s);
781
782        let zs = ZalgoString::new("").unwrap();
783        assert_eq!(zs.into_decoded_string(), "");
784    }
785
786    #[test]
787    fn check_string_from_zalgo_string() {
788        let zs = ZalgoString::new("Zalgo\n He comes!").unwrap();
789        assert_eq!(zs.to_string(), "É̺͇͌͏̨ͯ̀̀̓ͅ͏͍͓́ͅ");
790        assert_eq!(zs.into_string(), "É̺͇͌͏̨ͯ̀̀̓ͅ͏͍͓́ͅ");
791
792        let zs = ZalgoString::new("").unwrap();
793        assert_eq!(zs.into_string(), "E");
794    }
795
796    #[test]
797    fn check_partial_eq() {
798        let enc = "É̺͇͌͏̨ͯ̀̀̓ͅ͏͍͓́ͅ";
799        let zs = ZalgoString::new("Zalgo\n He comes!").unwrap();
800        assert_eq!(zs, enc);
801        assert_eq!(zs, String::from(enc));
802        assert_eq!(zs, Cow::from(enc));
803        assert_eq!(String::from(enc), zs);
804        assert_eq!(Cow::from(enc), zs);
805    }
806
807    #[test]
808    fn check_push_str() {
809        let s1 = "Zalgo";
810        let s2 = ", He comes";
811        let mut zs = ZalgoString::new(s1).unwrap();
812        let zs2 = ZalgoString::new(s2).unwrap();
813        zs.push_zalgo_str(&zs2);
814        assert_eq!(zs.clone().into_decoded_string(), format!("{s1}{s2}"));
815        zs += &zs2;
816        assert_eq!(
817            (zs + &zs2).into_decoded_string(),
818            format!("{s1}{s2}{s2}{s2}")
819        );
820    }
821
822    #[test]
823    fn check_as_combining_chars() {
824        assert_eq!(
825            ZalgoString::new("Hi").unwrap().as_combining_chars(),
826            "\u{328}\u{349}"
827        );
828        assert_eq!(ZalgoString::new("").unwrap().as_combining_chars(), "");
829    }
830
831    #[test]
832    fn check_decoded_chars() {
833        let zs = ZalgoString::new("Zalgo").unwrap();
834        assert_eq!("oglaZ", zs.decoded_chars().rev().collect::<String>());
835    }
836
837    #[test]
838    fn test_reserve() {
839        let mut zs = ZalgoString::new("Zalgo").unwrap();
840        zs.reserve(5);
841        assert!(zs.capacity() >= 11 + 5);
842        let c = zs.capacity();
843        zs.reserve(1);
844        assert_eq!(zs.capacity(), c);
845    }
846
847    #[test]
848    fn test_reserve_exact() {
849        let mut zs = ZalgoString::new("Zalgo").unwrap();
850        zs.reserve_exact(5);
851        assert_eq!(zs.capacity(), 11 + 5);
852        let c = zs.capacity();
853        zs.reserve_exact(1);
854        assert_eq!(zs.capacity(), c);
855    }
856
857    #[test]
858    fn test_truncate() {
859        let mut zs = ZalgoString::new("Zalgo").unwrap();
860        zs.truncate(100);
861        assert_eq!(zs, "E\u{33a}\u{341}\u{34c}\u{347}\u{34f}");
862        zs.truncate(5);
863        assert_eq!(zs, "E\u{33a}\u{341}");
864        assert_eq!(zs.into_decoded_string(), "Za");
865    }
866
867    #[test]
868    #[should_panic]
869    fn test_truncate_panic() {
870        let mut zs = ZalgoString::new("Zalgo").unwrap();
871        zs.truncate(0)
872    }
873
874    #[test]
875    fn test_default() {
876        assert_eq!(ZalgoString::new("").unwrap(), ZalgoString::default());
877    }
878
879    #[test]
880    fn test_with_capacity() {
881        let mut zs = ZalgoString::with_capacity(11.try_into().unwrap());
882        assert_eq!(zs.capacity(), 11);
883        zs.encode_and_push_str("Hi!").unwrap();
884        assert_eq!(zs.capacity(), 11);
885        zs.encode_and_push_str("I am a dinosaur!").unwrap();
886        assert!(zs.capacity() > 11);
887    }
888
889    #[test]
890    fn test_as_str() {
891        fn test_fn(_: &str) {}
892        let s = "Zalgo";
893        let zs = ZalgoString::new(s).unwrap();
894        let encd = zalgo_encode(s).unwrap();
895        test_fn(zs.as_str());
896        assert_eq!(zs.as_str(), encd);
897    }
898
899    #[test]
900    fn test_chars() {
901        let s = "Zalgo";
902        let zs = ZalgoString::new(s).unwrap();
903        let encd = zalgo_encode(s).unwrap();
904        for (a, b) in zs.chars().zip(encd.chars()) {
905            assert_eq!(a, b);
906        }
907        assert_eq!(zs.chars().next(), Some('E'));
908        assert_eq!(zs.chars().nth(2), Some('\u{341}'));
909    }
910
911    #[test]
912    fn test_char_indices() {
913        let s = "Zalgo";
914        let zs = ZalgoString::new(s).unwrap();
915        let encd = zalgo_encode(s).unwrap();
916        for (a, b) in zs.char_indices().zip(encd.char_indices()) {
917            assert_eq!(a, b);
918        }
919        assert_eq!(zs.char_indices().nth(2), Some((3, '\u{341}')));
920    }
921
922    #[test]
923    fn test_as_bytes() {
924        let zs = ZalgoString::new("Zalgo").unwrap();
925        assert_eq!(
926            zs.as_bytes(),
927            &[69, 204, 186, 205, 129, 205, 140, 205, 135, 205, 143]
928        );
929    }
930
931    #[test]
932    fn test_bytes() {
933        let zs = ZalgoString::new("Zalgo").unwrap();
934        assert_eq!(zs.bytes().next(), Some(69));
935        assert_eq!(zs.bytes().nth(2), Some(186));
936    }
937
938    #[test]
939    fn test_decoded_is_empty() {
940        let zs = ZalgoString::new("Zalgo").unwrap();
941        assert!(!zs.decoded_is_empty());
942        assert!(ZalgoString::default().decoded_is_empty());
943    }
944
945    #[test]
946    fn test_encode_and_push_str() {
947        let mut zs = ZalgoString::default();
948        assert!(zs.encode_and_push_str("Zalgo").is_ok());
949        assert!(zs.encode_and_push_str("Å").is_err());
950        assert_eq!(zs.into_decoded_string(), "Zalgo");
951    }
952
953    #[test]
954    fn test_clear() {
955        let mut zs = ZalgoString::new("Zalgo").unwrap();
956        let c = zs.capacity();
957        zs.clear();
958        assert_eq!(zs.capacity(), c);
959        assert_eq!(zs.len(), 1);
960        assert_eq!(zs.decoded_len(), 0);
961        assert!(zs.into_decoded_string().is_empty());
962    }
963
964    #[test]
965    fn test_get() {
966        let zs = ZalgoString::new("Zalgo").unwrap();
967        assert_eq!(zs.get(0..3), Some("E\u{33a}"));
968        assert!(zs.get(0..2).is_none());
969        assert!(zs.get(0..42).is_none());
970    }
971
972    #[test]
973    fn test_get_unchecked() {
974        let zs = ZalgoString::new("Zalgo").unwrap();
975        unsafe {
976            assert_eq!(zs.get_unchecked(..3), "E\u{33a}");
977        }
978    }
979
980    #[test]
981    fn test_indexing() {
982        let zs = ZalgoString::new("Zalgo").unwrap();
983        assert_eq!(&zs[0..3], "E\u{33a}");
984        assert_eq!(&zs[..3], "E\u{33a}");
985        assert_eq!(&zs[0..=2], "E\u{33a}");
986        assert_eq!(&zs[..=2], "E\u{33a}");
987        assert_eq!(zs[..], zs);
988    }
989
990    #[test]
991    #[should_panic]
992    fn test_index_panic() {
993        let zs = ZalgoString::new("Zalgo").unwrap();
994        let _a = &zs[0..2];
995    }
996
997    #[test]
998    fn test_decoded_bytes() {
999        let zs = ZalgoString::new("Zalgo").unwrap();
1000        assert_eq!(zs.decoded_bytes().next(), Some(b'Z'));
1001        assert_eq!(zs.decoded_bytes().nth(2), Some(b'l'));
1002        assert_eq!(zs.decoded_bytes().last(), Some(b'o'));
1003        let mut dcb = zs.decoded_bytes();
1004        assert_eq!(dcb.next(), Some(b'Z'));
1005        let dcb2 = dcb.clone();
1006        assert_eq!(dcb.count(), 4);
1007        assert_eq!(dcb2.last(), Some(b'o'));
1008    }
1009
1010    #[test]
1011    fn test_decoded_chars() {
1012        let zs = ZalgoString::new("Zalgo").unwrap();
1013        assert_eq!(zs.decoded_chars().next(), Some('Z'));
1014        assert_eq!(zs.decoded_chars().nth(2), Some('l'));
1015        assert_eq!(zs.decoded_chars().last(), Some('o'));
1016        let mut dcc = zs.decoded_chars();
1017        assert_eq!(dcc.next(), Some('Z'));
1018        let dcc2 = dcc.clone();
1019        assert_eq!(dcc.count(), 4);
1020        assert_eq!(dcc2.last(), Some('o'));
1021    }
1022
1023    #[test]
1024    fn test_into_combining_chars() {
1025        let zs = ZalgoString::new("Hi").unwrap();
1026        assert_eq!(zs.into_combining_chars(), "\u{328}\u{349}");
1027        let zs = ZalgoString::new("").unwrap();
1028        assert_eq!(zs.into_combining_chars(), "");
1029    }
1030}