bytes_text/
text.rs

1use std::{
2    borrow::Borrow,
3    convert::TryFrom,
4    fmt::{Debug, Display},
5    hash::Hash,
6    ops::{Deref, RangeBounds},
7    str::Utf8Error,
8};
9
10use bytes::Bytes;
11
12use crate::TextMut;
13
14/// Immutable, reference counted, UTF-8 text
15///
16/// # Example
17///
18/// ```
19/// # use bytes_text::Text;
20/// # use bytes::{Bytes, Buf};
21/// let mut s = String::from("Text, woo!");
22///
23/// let text = Text::from(s);
24/// assert_eq!(text, "Text, woo!");
25/// let (a, b) = text.split_at(5).unwrap();
26/// assert_eq!(a, "Text,");
27/// assert_eq!(b, " woo!");
28/// ```
29#[derive(Default, Clone)]
30pub struct Text(Bytes);
31
32impl Text {
33    /// Creates a new, empty, text buffer.
34    ///
35    /// # Example
36    ///
37    /// ```
38    /// # use bytes_text::Text;
39    /// let mut text = Text::new();
40    /// assert!(text.is_empty());
41    /// ```
42    pub fn new() -> Self {
43        Self(Bytes::new())
44    }
45
46    /// Converts `Bytes` to `Text`.
47    ///
48    /// # Example
49    ///
50    /// ```
51    /// # use bytes_text::Text;
52    /// # use bytes::{Bytes, Buf};
53    /// let mut buf = Bytes::from_static(&[
54    ///     0x69, 0x27, 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x61,
55    ///     0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x21
56    /// ]);
57    ///
58    /// let text = Text::from_utf8(buf).unwrap();
59    /// assert_eq!(text, "i'm in a buffer!");
60    /// ```
61    pub fn from_utf8(b: Bytes) -> Result<Self, Utf8Error> {
62        // run utf-8 validation
63        let _ = std::str::from_utf8(b.as_ref())?;
64        Ok(Self(b))
65    }
66
67    /// Converts `Bytes` to `Text` without verifying that it's valid UTF-8
68    ///
69    /// # Safety
70    ///
71    /// Must be valid UTF-8
72    ///
73    /// # Example
74    ///
75    /// ```
76    /// # use bytes_text::Text;
77    /// # use bytes::{Bytes, Buf};
78    /// let mut buf = Bytes::from_static(&[
79    ///     0x69, 0x27, 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x61,
80    ///     0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x21
81    /// ]);
82    ///
83    /// let text = unsafe { Text::from_utf8_unchecked(buf) };
84    /// assert_eq!(text, "i'm in a buffer!");
85    /// ```
86    #[inline]
87    pub const unsafe fn from_utf8_unchecked(b: Bytes) -> Self {
88        Self(b)
89    }
90
91    /// Copies the provided string into a new buffer.
92    ///
93    /// # Example
94    ///
95    /// ```
96    /// # use bytes_text::Text;
97    /// let mut s = format!("the answer is: {}", 42);
98    /// let text = Text::copy_from(s);
99    /// assert_eq!(text, "the answer is: 42");
100    /// ```
101    pub fn copy_from(s: impl AsRef<str>) -> Self {
102        // copy the bytes and wrap it
103        // guaranteed to be valid
104        Self(Bytes::copy_from_slice(s.as_ref().as_bytes()))
105    }
106
107    /// Creates `Text` from a static `str`
108    ///
109    /// # Example
110    ///
111    /// ```
112    /// # use bytes_text::Text;
113    /// let text = Text::from_static("Static!");
114    /// ```
115    /// Can also use `from`
116    /// ```
117    /// # use bytes_text::Text;
118    /// let text = Text::from("Also static!");
119    /// ```
120    pub const fn from_static(s: &'static str) -> Self {
121        Self(Bytes::from_static(s.as_bytes()))
122    }
123
124    /// The number of bytes in this text
125    ///
126    /// ```
127    /// # use bytes_text::Text;
128    /// let text = Text::from_static("Hello!");
129    /// assert_eq!(text.len(), 6);
130    /// ```
131    pub fn len(&self) -> usize {
132        self.0.len()
133    }
134
135    /// Checks if this text is empty
136    ///
137    /// # Example
138    ///
139    /// ```
140    /// # use bytes_text::Text;
141    /// let text = Text::new();
142    /// assert!(text.is_empty());
143    /// ```
144    pub fn is_empty(&self) -> bool {
145        self.0.is_empty()
146    }
147
148    /// Get a reference to the inner bytes
149    ///
150    /// # Example
151    ///
152    /// ```
153    /// # use bytes_text::Text;
154    /// # use bytes::Bytes;
155    /// let text = Text::from("Woah");
156    /// let bytes: &Bytes = text.as_bytes();
157    /// assert_eq!(bytes, &b"Woah"[..])
158    /// ```
159    pub const fn as_bytes(&self) -> &Bytes {
160        &self.0
161    }
162
163    /// Convert into bytes
164    ///
165    /// # Example
166    ///
167    /// ```
168    /// # use bytes_text::Text;
169    /// # use bytes::Bytes;
170    /// let text = Text::from("Woah");
171    /// let bytes: Bytes = text.into_bytes();
172    /// assert_eq!(&bytes, &b"Woah"[..])
173    /// ```
174    pub fn into_bytes(self) -> Bytes {
175        self.0
176    }
177
178    /// Get a sub-body of text
179    ///
180    /// # Example
181    ///
182    /// ```
183    /// # use bytes_text::Text;
184    /// # use bytes::Bytes;
185    /// let text = Text::from("Hi, I'm some text!");
186    /// let end = text.get(4..).unwrap();
187    /// assert_eq!(end, "I'm some text!");
188    /// let middle = text.get(8..12).unwrap();
189    /// assert_eq!(middle, "some");
190    /// ```
191    pub fn get(&self, r: impl RangeBounds<usize>) -> Option<Text> {
192        let start = match r.start_bound() {
193            std::ops::Bound::Included(&i) => i,
194            std::ops::Bound::Excluded(&i) => i.checked_add(1)?,
195            std::ops::Bound::Unbounded => 0,
196        };
197        let end = match r.end_bound() {
198            std::ops::Bound::Included(&i) => i.checked_add(1)?,
199            std::ops::Bound::Excluded(&i) => i,
200            std::ops::Bound::Unbounded => self.len(),
201        };
202        // str::is_char_boundary returns false if the index is out of bounds,
203        // so there's no need to check for it here
204        soft_assert::soft_assert!(self.is_char_boundary(start) && self.is_char_boundary(end));
205        Some(Self(self.0.slice(start..end)))
206    }
207
208    /// Splits the text into two halves
209    ///
210    /// Returns `Err(self)` if the index is not a valid char boundary
211    ///
212    /// # Example
213    ///
214    /// ```
215    /// # use bytes_text::Text;
216    /// let text = Text::from("Woo, split!");
217    /// let (a, b) = text.split_at(4).unwrap();
218    /// assert_eq!(a, "Woo,");
219    /// assert_eq!(b, " split!");
220    /// ```
221    pub fn split_at(mut self, index: usize) -> Result<(Self, Self), Self> {
222        soft_assert::soft_assert!(self.is_char_boundary(index), Err(self));
223        let right = self.0.split_off(index);
224        Ok((Self(self.0), Self(right)))
225    }
226
227    /// Splits the text into two halves, `self` being the start half and
228    /// returning the end half
229    ///
230    /// Returns `None` if the index is not a valid char boundary. If this
231    /// returns `None`, `self` remains unchanged.
232    ///
233    /// # Example
234    ///
235    /// ```
236    /// # use bytes_text::Text;
237    /// let mut text = Text::from("Woo, split!");
238    /// let end = text.split_off(4).unwrap();
239    /// assert_eq!(text, "Woo,");
240    /// assert_eq!(end, " split!");
241    /// ```
242    pub fn split_off(&mut self, index: usize) -> Option<Self> {
243        soft_assert::soft_assert!(self.is_char_boundary(index));
244        let right = self.0.split_off(index);
245        Some(Self(right))
246    }
247
248    /// Splits the text into two halves, `self` being the end half and
249    /// returning the start half
250    ///
251    /// Returns `None` if the index is not a valid char boundary. If this
252    /// returns `None`, `self` remains unchanged.
253    ///
254    /// # Example
255    ///
256    /// ```
257    /// # use bytes_text::Text;
258    /// let mut text = Text::from("Woo, split!");
259    /// let start = text.split_to(4).unwrap();
260    /// assert_eq!(start, "Woo,");
261    /// assert_eq!(text, " split!");
262    /// ```
263    pub fn split_to(&mut self, index: usize) -> Option<Self> {
264        soft_assert::soft_assert!(self.is_char_boundary(index));
265        let right = self.0.split_to(index);
266        Some(Self(right))
267    }
268
269    fn as_str(&self) -> &str {
270        unsafe { std::str::from_utf8_unchecked(self.0.as_ref()) }
271    }
272}
273
274// ## Conversions
275
276impl AsRef<str> for Text {
277    fn as_ref(&self) -> &str {
278        self.as_str()
279    }
280}
281
282impl Borrow<str> for Text {
283    fn borrow(&self) -> &str {
284        self.as_str()
285    }
286}
287
288impl Deref for Text {
289    type Target = str;
290
291    fn deref(&self) -> &Self::Target {
292        self.as_str()
293    }
294}
295
296impl From<&'static str> for Text {
297    fn from(s: &'static str) -> Self {
298        Self::from_static(s)
299    }
300}
301
302impl From<String> for Text {
303    fn from(s: String) -> Self {
304        Self(Bytes::from(s.into_bytes()))
305    }
306}
307
308impl TryFrom<Bytes> for Text {
309    type Error = Utf8Error;
310
311    fn try_from(b: Bytes) -> Result<Self, Self::Error> {
312        Self::from_utf8(b)
313    }
314}
315
316impl From<Text> for Bytes {
317    fn from(t: Text) -> Self {
318        t.into_bytes()
319    }
320}
321
322// ## Formatting
323
324impl Display for Text {
325    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326        Display::fmt(self.as_str(), f)
327    }
328}
329
330impl Debug for Text {
331    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332        Debug::fmt(self.as_str(), f)
333    }
334}
335
336// ## Comparisons
337
338// ### Self comparisons
339
340impl PartialEq for Text {
341    fn eq(&self, other: &Self) -> bool {
342        (&**self).eq(&**other)
343    }
344}
345
346impl Eq for Text {}
347
348impl PartialEq<&Text> for Text {
349    fn eq(&self, other: &&Text) -> bool {
350        (&**self).eq(&***other)
351    }
352}
353
354impl PartialEq<&mut Text> for Text {
355    fn eq(&self, other: &&mut Text) -> bool {
356        (&**self).eq(&***other)
357    }
358}
359
360impl PartialOrd for Text {
361    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
362        Some(self.cmp(other))
363    }
364}
365
366impl Ord for Text {
367    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
368        (&**self).cmp(&**other)
369    }
370}
371// ### str comparisons
372
373impl PartialEq<str> for Text {
374    fn eq(&self, other: &str) -> bool {
375        (&**self).eq(other)
376    }
377}
378
379impl PartialEq<&str> for Text {
380    fn eq(&self, other: &&str) -> bool {
381        (&**self).eq(*other)
382    }
383}
384
385impl PartialEq<&mut str> for Text {
386    fn eq(&self, other: &&mut str) -> bool {
387        (&**self).eq(*other)
388    }
389}
390
391impl PartialOrd<str> for Text {
392    fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> {
393        (&**self).partial_cmp(other)
394    }
395}
396
397impl PartialOrd<&str> for Text {
398    fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
399        (&**self).partial_cmp(*other)
400    }
401}
402
403impl PartialOrd<&mut str> for Text {
404    fn partial_cmp(&self, other: &&mut str) -> Option<std::cmp::Ordering> {
405        (&**self).partial_cmp(*other)
406    }
407}
408
409// ### String comparisons
410
411impl PartialEq<String> for Text {
412    fn eq(&self, other: &String) -> bool {
413        (&**self).eq(other)
414    }
415}
416
417impl PartialEq<&String> for Text {
418    fn eq(&self, other: &&String) -> bool {
419        (&**self).eq(*other)
420    }
421}
422
423impl PartialEq<&mut String> for Text {
424    fn eq(&self, other: &&mut String) -> bool {
425        (&**self).eq(*other)
426    }
427}
428
429impl PartialOrd<String> for Text {
430    fn partial_cmp(&self, other: &String) -> Option<std::cmp::Ordering> {
431        (&**self).partial_cmp(&**other)
432    }
433}
434
435impl PartialOrd<&String> for Text {
436    fn partial_cmp(&self, other: &&String) -> Option<std::cmp::Ordering> {
437        (&**self).partial_cmp(&***other)
438    }
439}
440
441impl PartialOrd<&mut String> for Text {
442    fn partial_cmp(&self, other: &&mut String) -> Option<std::cmp::Ordering> {
443        (&**self).partial_cmp(&***other)
444    }
445}
446
447// ### TextMut Comparisons
448
449impl PartialEq<TextMut> for Text {
450    fn eq(&self, other: &TextMut) -> bool {
451        (&**self).eq(&**other)
452    }
453}
454
455impl PartialEq<&TextMut> for Text {
456    fn eq(&self, other: &&TextMut) -> bool {
457        (&**self).eq(&***other)
458    }
459}
460
461impl PartialEq<&mut TextMut> for Text {
462    fn eq(&self, other: &&mut TextMut) -> bool {
463        (&**self).eq(&***other)
464    }
465}
466
467impl PartialOrd<TextMut> for Text {
468    fn partial_cmp(&self, other: &TextMut) -> Option<std::cmp::Ordering> {
469        (&**self).partial_cmp(&**other)
470    }
471}
472
473impl PartialOrd<&TextMut> for Text {
474    fn partial_cmp(&self, other: &&TextMut) -> Option<std::cmp::Ordering> {
475        (&**self).partial_cmp(&***other)
476    }
477}
478
479impl PartialOrd<&mut TextMut> for Text {
480    fn partial_cmp(&self, other: &&mut TextMut) -> Option<std::cmp::Ordering> {
481        (&**self).partial_cmp(&***other)
482    }
483}
484
485// ## Hash
486
487impl Hash for Text {
488    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
489        (&**self).hash(state);
490    }
491}