bytes_text/text_mut.rs
1use std::{
2 borrow::{Borrow, BorrowMut},
3 convert::TryFrom,
4 fmt::{Debug, Display},
5 hash::Hash,
6 ops::{Deref, DerefMut},
7 str::Utf8Error,
8};
9
10use bytes::BytesMut;
11
12use crate::Text;
13
14/// Mutable UTF-8 text buffer
15///
16/// # Examples
17///
18/// ```
19/// use bytes_text::TextMut;
20///
21/// let mut text = TextMut::with_capacity(64);
22///
23/// text.push('h'); // `push` adds a character to the end of the text
24/// text.push('e');
25/// text.push_str("llo");
26///
27/// assert_eq!(text, "hello");
28///
29/// // Freeze the buffer so that it can be shared
30/// let a = text.freeze();
31///
32/// // This does not allocate, instead `b` points to the same memory.
33/// let b = a.clone();
34///
35/// assert_eq!(a, "hello");
36/// assert_eq!(b, "hello");
37/// ```
38// example taken from `bytes`
39#[derive(Default)]
40pub struct TextMut(BytesMut);
41
42impl TextMut {
43 /// Creates a new, empty, text buffer.
44 ///
45 /// # Example
46 ///
47 /// ```
48 /// # use bytes_text::TextMut;
49 /// let mut text = TextMut::new();
50 /// text.push_str("Hello!");
51 /// println!("{}", text);
52 /// ```
53 pub fn new() -> Self {
54 Self(BytesMut::new())
55 }
56
57 /// Creates a new, empty, text buffer that can grow to at least `capacity`
58 /// bytes long before reallocating
59 ///
60 /// # Example
61 ///
62 /// ```
63 /// # use bytes_text::TextMut;
64 /// let mut text = TextMut::with_capacity(6);
65 /// text.push_str("Hello!"); // Doesn't allocate
66 /// println!("{}", text);
67 /// ```
68 pub fn with_capacity(capacity: usize) -> Self {
69 Self(BytesMut::with_capacity(capacity))
70 }
71
72 /// Copies the provided string into a new mutable buffer.
73 ///
74 /// # Example
75 ///
76 /// ```
77 /// # use bytes_text::TextMut;
78 /// let mut text = TextMut::copy_from("Hello,");
79 /// text.push_str(" world!");
80 /// println!("{}", text );
81 /// ```
82 pub fn copy_from(s: impl AsRef<str>) -> Self {
83 // There is no `BytesMut::copy_from_slice`
84 let s = s.as_ref();
85 let mut t = Self::with_capacity(s.len());
86 t.push_str(s);
87 t
88 }
89
90 /// Converts `Bytes` to `Text`.
91 ///
92 /// # Example
93 ///
94 /// ```
95 /// # use bytes_text::TextMut;
96 /// # use bytes::{BytesMut, BufMut};
97 /// let mut buf = BytesMut::new();
98 /// buf.put(&b"Hello,"[..]);
99 ///
100 /// let mut text = TextMut::from_utf8(buf).expect("Invalid UTF-8!");
101 /// text.push_str(" world!");
102 /// assert_eq!(text, "Hello, world!");
103 /// ```
104 pub fn from_utf8(b: BytesMut) -> Result<Self, Utf8Error> {
105 // run utf-8 validation
106 let _ = std::str::from_utf8(b.as_ref())?;
107 Ok(Self(b))
108 }
109
110 /// Converts `Bytes` to `Text` without verifying that it's valid UTF-8
111 ///
112 /// # Safety
113 ///
114 /// Must be valid UTF-8
115 ///
116 /// # Example
117 ///
118 /// ```
119 /// # use bytes_text::TextMut;
120 /// # use bytes::{BytesMut, BufMut};
121 /// let mut buf = BytesMut::new();
122 /// buf.put(&b"Hello,"[..]);
123 ///
124 /// // We can see above that it is valid
125 /// let mut text = unsafe { TextMut::from_utf8_unchecked(buf) };
126 /// text.push_str(" world!");
127 /// assert_eq!(text, "Hello, world!");
128 /// ```
129 #[inline]
130 pub const unsafe fn from_utf8_unchecked(b: BytesMut) -> Self {
131 Self(b)
132 }
133
134 /// The number of bytes in this text
135 ///
136 /// # Example
137 ///
138 /// ```
139 /// # use bytes_text::TextMut;
140 /// let text = TextMut::copy_from("Hello!");
141 /// assert_eq!(text.len(), 6);
142 /// ```
143 pub fn len(&self) -> usize {
144 self.0.len()
145 }
146
147 /// The maximum length of this buffer before reallocation is required
148 ///
149 /// # Example
150 ///
151 /// ```
152 /// # use bytes_text::TextMut;
153 /// let mut text = TextMut::with_capacity(32);
154 /// text.push_str("rustlang");
155 /// assert_eq!(text.len(), 8);
156 /// assert_eq!(text.capacity(), 32);
157 /// ```
158 pub fn capacity(&self) -> usize {
159 self.0.capacity()
160 }
161
162 /// Checks if this text is empty
163 ///
164 /// # Example
165 ///
166 /// ```
167 /// # use bytes_text::TextMut;
168 /// let text = TextMut::new();
169 /// assert!(text.is_empty());
170 ///
171 /// // Even if there's available capacity
172 /// let text2 = TextMut::with_capacity(24);
173 /// assert!(text.is_empty());
174 /// ```
175 pub fn is_empty(&self) -> bool {
176 self.0.is_empty()
177 }
178
179 /// Freezes this into an immutable, shareable, text buffer.
180 ///
181 /// # Example
182 ///
183 /// ```
184 /// # use bytes_text::TextMut;
185 ///
186 /// let mut text = TextMut::with_capacity(64);
187 /// text.push_str("hello");
188 ///
189 /// // Freeze the buffer so that it can be shared
190 /// let a = text.freeze();
191 ///
192 /// // This does not allocate, instead `b` points to the same memory.
193 /// let b = a.clone();
194 ///
195 /// assert_eq!(a, "hello");
196 /// assert_eq!(b, "hello");
197 /// ```
198 pub fn freeze(self) -> Text {
199 // Safety: self.0 is guaranteed to be valid UTF-8
200 unsafe { Text::from_utf8_unchecked(self.0.freeze()) }
201 }
202
203 /// Reserves space for at least `additional` more bytes to be inserted
204 ///
205 /// # Example
206 ///
207 /// ```
208 /// # use bytes_text::TextMut;
209 /// let mut text = TextMut::new();
210 /// text.reserve(24);
211 /// assert_eq!(text.capacity(), 24);
212 /// ```
213 pub fn reserve(&mut self, additional: usize) {
214 self.0.reserve(additional)
215 }
216
217 /// Clears the buffer of its contents
218 ///
219 /// # Example
220 ///
221 /// ```
222 /// # use bytes_text::TextMut;
223 /// let mut text = TextMut::copy_from("I'm gonna get cleared! ðŸ˜");
224 /// text.clear();
225 /// assert_eq!(text.len(), 0);
226 /// assert_eq!(text, TextMut::new());
227 /// // Capacity is conserved
228 /// assert!(text.capacity() > 0);
229 /// ```
230 pub fn clear(&mut self) {
231 self.0.clear()
232 }
233
234 /// Get a reference to the inner bytes
235 ///
236 /// # Example
237 ///
238 /// ```
239 /// # use bytes_text::TextMut;
240 /// # use bytes::BytesMut;
241 /// let text = TextMut::copy_from("Woah");
242 /// let bytes: &BytesMut = text.as_bytes();
243 /// ```
244 pub const fn as_bytes(&self) -> &BytesMut {
245 &self.0
246 }
247
248 /// Get a mutable reference to the inner bytes
249 ///
250 /// # Safety
251 ///
252 /// The returned reference must not be modified in such a way that makes it
253 /// invalid UTF-8, even momentarily
254 ///
255 /// # Example
256 ///
257 /// ```
258 /// # use bytes_text::TextMut;
259 /// # use bytes::{BytesMut, BufMut};
260 /// let mut text = TextMut::copy_from("Hello");
261 /// // Must not modify it in a way that makes it invalid UTF-8
262 ///
263 /// let bytes: &mut BytesMut = unsafe { text.as_bytes_mut() };
264 /// bytes.put_u8(b'!');
265 /// drop(bytes); // not necessarily needed
266 ///
267 /// assert_eq!(text, "Hello!");
268 /// ```
269 pub unsafe fn as_bytes_mut(&mut self) -> &mut BytesMut {
270 &mut self.0
271 }
272
273 /// Convert into a mutable buffer of raw bytes
274 ///
275 /// # Example
276 ///
277 /// ```
278 /// # use bytes_text::TextMut;
279 /// # use bytes::{BytesMut, BufMut};
280 /// let text = TextMut::copy_from("Hello");
281 ///
282 /// // Must not modify it in a way that makes it invalid UTF-8
283 /// let mut bytes: BytesMut = text.into_bytes_mut();
284 /// bytes.put_u8(b'!');
285 ///
286 /// /// Everything we did was ok :)
287 /// let text = unsafe { TextMut::from_utf8_unchecked(bytes) };
288 /// assert_eq!(text, "Hello!");
289 /// ```
290 pub fn into_bytes_mut(self) -> BytesMut {
291 self.0
292 }
293
294 /// Splits the text into two halves
295 ///
296 /// Returns `Err(self)` if the index is not a valid char boundary
297 ///
298 /// # Example
299 ///
300 /// ```
301 /// # use bytes_text::TextMut;
302 /// let mut text = TextMut::copy_from("Woo, split!");
303 /// let (a, b) = text.split_at(4).unwrap();
304 /// assert_eq!(a, "Woo,");
305 /// assert_eq!(b, " split!");
306 /// ```
307 pub fn split_at(mut self, index: usize) -> Result<(Self, Self), Self> {
308 soft_assert::soft_assert!(self.is_char_boundary(index), Err(self));
309 let right = self.0.split_off(index);
310 Ok((Self(self.0), Self(right)))
311 }
312
313 /// Splits the text into two halves, `self` being the start half and
314 /// returning the end half
315 ///
316 /// Returns `None` if the index is not a valid char boundary. If this
317 /// returns `None`, `self` remains unchanged.
318 ///
319 /// # Example
320 ///
321 /// ```
322 /// # use bytes_text::TextMut;
323 /// let mut text = TextMut::copy_from("Woo, split!");
324 /// let end = text.split_off(4).unwrap();
325 /// assert_eq!(text, "Woo,");
326 /// assert_eq!(end, " split!");
327 /// ```
328 pub fn split_off(&mut self, index: usize) -> Option<Self> {
329 soft_assert::soft_assert!(self.is_char_boundary(index));
330 let right = self.0.split_off(index);
331 Some(Self(right))
332 }
333
334 /// Splits the text into two halves, `self` being the end half and
335 /// returning the start half
336 ///
337 /// Returns `None` if the index is not a valid char boundary. If this
338 /// returns `None`, `self` remains unchanged.
339 ///
340 /// # Example
341 ///
342 /// ```
343 /// # use bytes_text::TextMut;
344 /// let mut text = TextMut::copy_from("Woo, split!");
345 /// let start = text.split_to(4).unwrap();
346 /// assert_eq!(start, "Woo,");
347 /// assert_eq!(text, " split!");
348 /// ```
349 pub fn split_to(&mut self, index: usize) -> Option<Self> {
350 soft_assert::soft_assert!(self.is_char_boundary(index));
351 let right = self.0.split_to(index);
352 Some(Self(right))
353 }
354
355 /// Copies the string reference into this buffer
356 ///
357 /// If you're pushing another `TextMut`, it's better to use [`TextMut::join`](TextMut::join)
358 ///
359 /// # Example
360 ///
361 /// ```
362 /// # use bytes_text::TextMut;
363 /// let mut text = TextMut::new();
364 /// text.push_str("Hello, ");
365 /// text.push_str("world! ");
366 ///
367 /// // Works with `String` too
368 /// let string = String::from("i'm in a string");
369 /// text.push_str(string);
370 ///
371 /// assert_eq!(text, "Hello, world! i'm in a string");
372 /// ```
373 pub fn push_str(&mut self, s: impl AsRef<str>) {
374 self.0.extend_from_slice(s.as_ref().as_bytes())
375 }
376
377 /// Adds a character to the end of this buffer
378 ///
379 /// # Example
380 ///
381 /// ```
382 /// # use bytes_text::TextMut;
383 /// let mut text = TextMut::new();
384 /// text.push('H');
385 /// text.push('e');
386 /// text.push('l');
387 /// text.push('l');
388 /// text.push('o');
389 /// assert_eq!(text, "Hello");
390 /// ```
391 pub fn push(&mut self, c: char) {
392 let mut buf = [0; 4];
393 let s = c.encode_utf8(&mut buf);
394 self.push_str(s);
395 }
396
397 /// Joins two `TextMut`s together
398 ///
399 /// If they were once contiguous (i.e. from one of the `split` methods) then
400 /// this takes O(1) time
401 ///
402 /// # Examples
403 ///
404 /// ```
405 /// # use bytes_text::TextMut;
406 /// let mut a = TextMut::copy_from("Oh ");
407 /// let b = TextMut::copy_from("heck");
408 ///
409 /// // Allocates more space on `a` to make room for `b`
410 /// let joined = a.join(b);
411 /// assert_eq!(joined, "Oh heck");
412 /// ```
413 ///
414 /// ```
415 /// # use bytes_text::TextMut;
416 /// let mut text = TextMut::copy_from("woohoo");
417 ///
418 /// let (mut a, b) = text.split_at(3).unwrap();
419 /// assert_eq!(a, "woo");
420 /// assert_eq!(b, "hoo");
421 ///
422 /// // Doesn't allocates more space, since they come from the same buffer
423 /// let joined = a.join(b);
424 /// assert_eq!(joined, "woohoo");
425 /// ```
426 pub fn join(mut self, other: TextMut) -> TextMut {
427 self.0.unsplit(other.0);
428 self
429 }
430
431 fn as_str(&self) -> &str {
432 // Safety:
433 // `self` will always contain valid UTF-8
434 unsafe { std::str::from_utf8_unchecked(self.0.as_ref()) }
435 }
436
437 fn as_str_mut(&mut self) -> &mut str {
438 unsafe { std::str::from_utf8_unchecked_mut(self.0.as_mut()) }
439 }
440}
441
442// ## Conversions
443
444impl AsRef<str> for TextMut {
445 fn as_ref(&self) -> &str {
446 self.as_str()
447 }
448}
449
450impl AsMut<str> for TextMut {
451 fn as_mut(&mut self) -> &mut str {
452 self.as_str_mut()
453 }
454}
455
456impl Borrow<str> for TextMut {
457 fn borrow(&self) -> &str {
458 self.as_str()
459 }
460}
461
462impl BorrowMut<str> for TextMut {
463 fn borrow_mut(&mut self) -> &mut str {
464 self.as_str_mut()
465 }
466}
467
468impl Deref for TextMut {
469 type Target = str;
470
471 fn deref(&self) -> &Self::Target {
472 self.as_str()
473 }
474}
475
476impl DerefMut for TextMut {
477 fn deref_mut(&mut self) -> &mut Self::Target {
478 self.as_str_mut()
479 }
480}
481
482impl TryFrom<BytesMut> for TextMut {
483 type Error = Utf8Error;
484
485 fn try_from(b: BytesMut) -> Result<Self, Self::Error> {
486 Self::from_utf8(b)
487 }
488}
489
490impl From<TextMut> for BytesMut {
491 fn from(t: TextMut) -> Self {
492 t.into_bytes_mut()
493 }
494}
495
496impl From<TextMut> for Text {
497 fn from(t: TextMut) -> Self {
498 t.freeze()
499 }
500}
501
502// ## Formatting
503
504impl Display for TextMut {
505 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
506 Display::fmt(self.as_str(), f)
507 }
508}
509
510impl Debug for TextMut {
511 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
512 Debug::fmt(self.as_str(), f)
513 }
514}
515
516// ## Comparisons
517
518// ### Self comparisons
519
520impl PartialEq for TextMut {
521 fn eq(&self, other: &Self) -> bool {
522 (&**self).eq(&**other)
523 }
524}
525
526impl Eq for TextMut {}
527
528impl PartialEq<&TextMut> for TextMut {
529 fn eq(&self, other: &&TextMut) -> bool {
530 (&**self).eq(&***other)
531 }
532}
533
534impl PartialEq<&mut TextMut> for TextMut {
535 fn eq(&self, other: &&mut TextMut) -> bool {
536 (&**self).eq(&***other)
537 }
538}
539
540impl PartialOrd for TextMut {
541 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
542 Some(self.cmp(other))
543 }
544}
545
546// ### str comparisons
547
548impl PartialEq<str> for TextMut {
549 fn eq(&self, other: &str) -> bool {
550 (&**self).eq(other)
551 }
552}
553
554impl PartialEq<&str> for TextMut {
555 fn eq(&self, other: &&str) -> bool {
556 (&**self).eq(*other)
557 }
558}
559
560impl PartialEq<&mut str> for TextMut {
561 fn eq(&self, other: &&mut str) -> bool {
562 (&**self).eq(*other)
563 }
564}
565
566impl PartialOrd<str> for TextMut {
567 fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> {
568 (&**self).partial_cmp(other)
569 }
570}
571
572impl PartialOrd<&str> for TextMut {
573 fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
574 (&**self).partial_cmp(*other)
575 }
576}
577
578impl PartialOrd<&mut str> for TextMut {
579 fn partial_cmp(&self, other: &&mut str) -> Option<std::cmp::Ordering> {
580 (&**self).partial_cmp(*other)
581 }
582}
583
584// ### String comparisons
585
586impl PartialEq<String> for TextMut {
587 fn eq(&self, other: &String) -> bool {
588 (&**self).eq(other)
589 }
590}
591
592impl PartialEq<&String> for TextMut {
593 fn eq(&self, other: &&String) -> bool {
594 (&**self).eq(*other)
595 }
596}
597
598impl PartialEq<&mut String> for TextMut {
599 fn eq(&self, other: &&mut String) -> bool {
600 (&**self).eq(*other)
601 }
602}
603
604impl PartialOrd<String> for TextMut {
605 fn partial_cmp(&self, other: &String) -> Option<std::cmp::Ordering> {
606 (&**self).partial_cmp(&**other)
607 }
608}
609
610impl PartialOrd<&String> for TextMut {
611 fn partial_cmp(&self, other: &&String) -> Option<std::cmp::Ordering> {
612 (&**self).partial_cmp(&***other)
613 }
614}
615
616impl PartialOrd<&mut String> for TextMut {
617 fn partial_cmp(&self, other: &&mut String) -> Option<std::cmp::Ordering> {
618 (&**self).partial_cmp(&***other)
619 }
620}
621
622// ### Text comparisons
623
624impl PartialEq<Text> for TextMut {
625 fn eq(&self, other: &Text) -> bool {
626 (&**self).eq(&**other)
627 }
628}
629
630impl PartialEq<&Text> for TextMut {
631 fn eq(&self, other: &&Text) -> bool {
632 (&**self).eq(&***other)
633 }
634}
635
636impl PartialEq<&mut Text> for TextMut {
637 fn eq(&self, other: &&mut Text) -> bool {
638 (&**self).eq(&***other)
639 }
640}
641
642impl PartialOrd<Text> for TextMut {
643 fn partial_cmp(&self, other: &Text) -> Option<std::cmp::Ordering> {
644 (&**self).partial_cmp(&**other)
645 }
646}
647
648impl PartialOrd<&Text> for TextMut {
649 fn partial_cmp(&self, other: &&Text) -> Option<std::cmp::Ordering> {
650 (&**self).partial_cmp(&***other)
651 }
652}
653
654impl PartialOrd<&mut Text> for TextMut {
655 fn partial_cmp(&self, other: &&mut Text) -> Option<std::cmp::Ordering> {
656 (&**self).partial_cmp(&***other)
657 }
658}
659
660/// ## Hash
661
662impl Hash for TextMut {
663 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
664 (&**self).hash(state);
665 }
666}
667
668/// ## Extend
669
670impl Extend<char> for TextMut {
671 fn extend<T>(&mut self, iter: T)
672 where
673 T: IntoIterator<Item = char>,
674 {
675 let iterator = iter.into_iter();
676 let (lower_bound, _) = iterator.size_hint();
677 self.reserve(lower_bound);
678 iterator.for_each(move |c| self.push(c));
679 }
680}
681
682impl<'a> Extend<&'a str> for TextMut {
683 fn extend<T>(&mut self, iter: T)
684 where
685 T: IntoIterator<Item = &'a str>,
686 {
687 iter.into_iter().for_each(move |s| self.push_str(s));
688 }
689}
690
691impl Extend<String> for TextMut {
692 fn extend<T>(&mut self, iter: T)
693 where
694 T: IntoIterator<Item = String>,
695 {
696 iter.into_iter().for_each(move |s| self.push_str(s));
697 }
698}
699
700impl<'a> Extend<&'a String> for TextMut {
701 fn extend<T>(&mut self, iter: T)
702 where
703 T: IntoIterator<Item = &'a String>,
704 {
705 iter.into_iter().for_each(move |s| self.push_str(s));
706 }
707}
708
709impl Extend<Text> for TextMut {
710 fn extend<T>(&mut self, iter: T)
711 where
712 T: IntoIterator<Item = Text>,
713 {
714 iter.into_iter().for_each(move |s| self.push_str(s));
715 }
716}
717
718impl<'a> Extend<&'a Text> for TextMut {
719 fn extend<T>(&mut self, iter: T)
720 where
721 T: IntoIterator<Item = &'a Text>,
722 {
723 iter.into_iter().for_each(move |s| self.push_str(s));
724 }
725}
726
727#[cfg(test)]
728mod tests {
729 use super::*;
730
731 #[test]
732 fn text_mut_create() {
733 let mut buf = TextMut::new();
734 buf.push_str("Hello, ");
735 buf.push_str("world!");
736 assert_eq!(buf, "Hello, world!");
737 buf.push(' ');
738 buf.extend(vec!["Woo, ", "unit tests!"]);
739 assert_eq!(buf, "Hello, world! Woo, unit tests!");
740 buf.extend(vec![String::from(" I'm in a String.")]);
741 buf.extend(vec![Text::from(" Ooo, text")]);
742 assert_eq!(
743 format!("{}", buf),
744 String::from("Hello, world! Woo, unit tests! I'm in a String. Ooo, text")
745 );
746 buf.clear();
747 assert_eq!(buf, "");
748 }
749}