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}