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}