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