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}