toad_string/
lib.rs

1//! This microcrate contains the stack-allocated `String` struct used by the
2//! [`toad`](https://github.com/toad-lib/toad) CoAP runtime / ecosystem.
3
4// docs
5#![doc(html_root_url = "https://docs.rs/toad-string/0.0.0")]
6#![cfg_attr(any(docsrs, feature = "docs"), feature(doc_cfg))]
7// -
8// style
9#![allow(clippy::unused_unit)]
10// -
11// deny
12#![deny(missing_docs)]
13#![deny(missing_debug_implementations)]
14#![deny(missing_copy_implementations)]
15#![cfg_attr(not(test), deny(unsafe_code))]
16// -
17// warnings
18#![cfg_attr(not(test), warn(unreachable_pub))]
19// -
20// features
21#![cfg_attr(not(feature = "std"), no_std)]
22
23#[cfg(feature = "alloc")]
24extern crate alloc as std_alloc;
25
26use core::fmt::{Display, Write};
27use core::ops::{Deref, DerefMut};
28
29use tinyvec::ArrayVec;
30use toad_array::AppendCopy;
31use toad_len::Len;
32use toad_writable::Writable;
33
34#[allow(missing_docs)]
35#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Default)]
36pub struct FromUtf8Error;
37
38#[allow(missing_docs)]
39#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Default)]
40pub struct FromUtf16Error;
41
42/// [`String`]-returning copy of [`std::format`]
43///
44/// ```
45/// use toad_string::{format, String};
46/// assert_eq!(format!(32, "hello, {}!", String::<5>::from("jason")),
47///            String::<32>::from("hello, jason!"));
48/// ```
49#[macro_export]
50macro_rules! format {
51  ($cap:literal, $($arg:tt)*) => {
52    $crate::String::<$cap>::fmt(format_args!($($arg)*))
53  };
54}
55
56/// Stack-allocated UTF-8 string with a fixed capacity.
57///
58/// Has many of the same inherent functions as [`std::string::String`].
59#[derive(Debug, Copy, Clone, Default)]
60pub struct String<const N: usize>(Writable<ArrayVec<[u8; N]>>);
61
62impl<const N: usize> String<N> {
63  /// Creates a new string with the specified capacity
64  pub fn new() -> Self {
65    Default::default()
66  }
67
68  /// Gets a string slice containing the entire [`String`]
69  pub fn as_str(&self) -> &str {
70    self.as_ref()
71  }
72
73  /// Convert the [`String`] to a mutable string slice
74  pub fn as_mut_str(&mut self) -> &mut str {
75    self.as_mut()
76  }
77
78  /// Resize the String to a new length
79  ///
80  /// If `M` is less than `N`, the extra bytes are
81  /// discarded.
82  pub fn resize<const M: usize>(&mut self) -> String<M> {
83    let mut bytes = self.0.unwrap();
84    bytes.truncate(M);
85    String(Writable::from(self.as_writable().drain(..).collect::<ArrayVec<[u8; M]>>()))
86  }
87
88  /// Alias for [`AsRef`]
89  pub fn as_bytes(&self) -> &[u8] {
90    self.as_ref()
91  }
92
93  /// Get a mutable reference to the inner writable buffer
94  pub fn as_writable(&mut self) -> &mut Writable<ArrayVec<[u8; N]>> {
95    &mut self.0
96  }
97
98  /// Creates a [`String`] using the output of [`format_args`]
99  pub fn fmt(args: core::fmt::Arguments) -> Self {
100    let mut s = Self::new();
101    s.write_fmt(args).ok();
102    s
103  }
104
105  /// Returns this [`String`]'s capacity, in bytes.
106  pub fn capacity(&self) -> usize {
107    N
108  }
109
110  /// Truncates this [`String`], removing all contents.
111  pub fn clear(&mut self) {
112    self.0.clear()
113  }
114
115  /// Copy a slice of bytes to a `String`.
116  ///
117  /// A string ([`String`]) is made of bytes ([`u8`]), and a vector of bytes
118  /// ([`Vec<u8>`]) is made of bytes, so this function converts between the
119  /// two. Not all byte slices are valid `String`s, however: `String`
120  /// requires that it is valid UTF-8. `from_utf8()` checks to ensure that
121  /// the bytes are valid UTF-8, and then does the conversion.
122  ///
123  /// # Errors
124  ///
125  /// Returns [`Err`] if the slice is not UTF-8 with a description as to why the
126  /// provided bytes are not UTF-8. The vector you moved in is also included.
127  ///
128  /// # Examples
129  ///
130  /// Basic usage:
131  ///
132  /// ```
133  /// use toad_string::String;
134  ///
135  /// // some bytes, in a vector
136  /// let sparkle_heart = vec![240, 159, 146, 150];
137  ///
138  /// // We know these bytes are valid, so we'll use `unwrap()`.
139  /// let sparkle_heart = String::<16>::from_utf8(&sparkle_heart).unwrap();
140  ///
141  /// assert_eq!("💖", sparkle_heart);
142  /// ```
143  ///
144  /// Incorrect bytes:
145  ///
146  /// ```
147  /// use toad_string::String;
148  ///
149  /// // some invalid bytes, in a vector
150  /// let sparkle_heart = vec![0, 159, 146, 150];
151  ///
152  /// assert!(String::<16>::from_utf8(&sparkle_heart).is_err());
153  /// ```
154  ///
155  /// [`Vec<u8>`]: std::vec::Vec "Vec"
156  /// [`&str`]: prim@str "&str"
157  #[inline]
158  pub fn from_utf8(bytes: &[u8]) -> Result<Self, FromUtf8Error> {
159    match core::str::from_utf8(bytes) {
160      | Ok(s) => Ok(Self::from(s)),
161      | Err(_) => Err(FromUtf8Error),
162    }
163  }
164
165  /// Decode a UTF-16–encoded vector `v` into a `String`, returning [`Err`]
166  /// if `v` contains any invalid data.
167  ///
168  /// # Examples
169  ///
170  /// Basic usage:
171  ///
172  /// ```
173  /// use toad_string::String;
174  ///
175  /// // 𝄞music
176  /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
177  /// assert_eq!(String::<16>::from("𝄞music"),
178  ///            String::<16>::from_utf16(v).unwrap());
179  ///
180  /// // 𝄞mu<invalid>ic
181  /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
182  /// assert!(String::<16>::from_utf16(v).is_err());
183  /// ```
184  pub fn from_utf16(v: &[u16]) -> Result<Self, FromUtf16Error> {
185    let mut ret = String::new();
186    for c in char::decode_utf16(v.iter().cloned()) {
187      if let Ok(c) = c {
188        ret.push(c);
189      } else {
190        return Err(FromUtf16Error);
191      }
192    }
193    Ok(ret)
194  }
195
196  /// Inserts a string slice into this `String` at a byte position.
197  ///
198  /// This is an *O*(*n*) operation as it requires copying every element in the
199  /// buffer.
200  ///
201  /// # Panics
202  ///
203  /// Panics if `idx` is larger than the `String`'s length, or if it does not
204  /// lie on a [`char`] boundary.
205  ///
206  /// # Examples
207  ///
208  /// Basic usage:
209  ///
210  /// ```
211  /// use toad_string::String;
212  ///
213  /// let mut s = String::<16>::from("bar");
214  ///
215  /// s.insert_str(0, "foo");
216  ///
217  /// assert_eq!("foobar", s);
218  /// ```
219  #[inline]
220  pub fn insert_str(&mut self, idx: usize, string: &str) {
221    assert!(self.is_char_boundary(idx));
222
223    for (i, b) in string.bytes().enumerate() {
224      self.0.insert(idx + i, b);
225    }
226  }
227
228  /// Inserts a character into this `String` at a byte position.
229  ///
230  /// This is an *O*(*n*) operation as it requires copying every element in the
231  /// buffer.
232  ///
233  /// # Panics
234  ///
235  /// Panics if `idx` is larger than the `String`'s length, or if it does not
236  /// lie on a [`char`] boundary.
237  ///
238  /// # Examples
239  ///
240  /// Basic usage:
241  ///
242  /// ```
243  /// use toad_string::String;
244  ///
245  /// let mut s = String::<16>::new();
246  ///
247  /// s.insert(0, 'f');
248  /// s.insert(1, 'o');
249  /// s.insert(2, 'o');
250  ///
251  /// assert_eq!("foo", s);
252  /// ```
253  #[inline]
254  pub fn insert(&mut self, idx: usize, ch: char) {
255    assert!(self.is_char_boundary(idx));
256    let mut bits = [0; 4];
257    let bits = ch.encode_utf8(&mut bits).as_bytes();
258
259    for (i, b) in bits.iter().enumerate() {
260      self.0.insert(idx + i, *b);
261    }
262  }
263
264  /// Appends the given [`char`] to the end of this `String`.
265  ///
266  /// # Examples
267  ///
268  /// Basic usage:
269  ///
270  /// ```
271  /// use toad_string::String;
272  ///
273  /// let mut s = String::<16>::from("abc");
274  ///
275  /// s.push('1');
276  /// s.push('2');
277  /// s.push('3');
278  ///
279  /// assert_eq!("abc123", s);
280  /// ```
281  pub fn push(&mut self, ch: char) {
282    match ch.len_utf8() {
283      | 1 => self.0.push(ch as u8),
284      | _ => self.0
285                 .extend_from_slice(ch.encode_utf8(&mut [0; 4]).as_bytes()),
286    }
287  }
288
289  /// Appends a given string slice onto the end of this `String`.
290  ///
291  /// # Examples
292  ///
293  /// Basic usage:
294  ///
295  /// ```
296  /// use toad_string::String;
297  ///
298  /// let mut s = String::<16>::from("foo");
299  ///
300  /// s.push_str("bar");
301  ///
302  /// assert_eq!("foobar", s);
303  /// ```
304  #[inline]
305  pub fn push_str(&mut self, string: &str) {
306    self.0.append_copy(string.as_bytes())
307  }
308}
309
310impl<const N: usize> Len for String<N> {
311  const CAPACITY: Option<usize> = Some(N);
312
313  fn len(&self) -> usize {
314    self.0.len()
315  }
316
317  fn is_full(&self) -> bool {
318    self.0.is_full()
319  }
320}
321
322impl<const N: usize> Display for String<N> {
323  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
324    write!(f, "{}", self.as_str())
325  }
326}
327
328impl<const N: usize> PartialEq for String<N> {
329  fn eq(&self, other: &Self) -> bool {
330    self.0.as_str() == other.0.as_str()
331  }
332}
333
334impl<const N: usize> Eq for String<N> {}
335
336impl<const N: usize> core::fmt::Write for String<N> {
337  fn write_str(&mut self, s: &str) -> core::fmt::Result {
338    self.0.write_str(s)
339  }
340}
341
342impl<'a, const N: usize> From<&'a str> for String<N> {
343  fn from(s: &'a str) -> Self {
344    let mut arr = Writable::default();
345    ArrayVec::extend_from_slice(&mut arr, s.as_bytes());
346
347    Self(arr)
348  }
349}
350
351impl<const N: usize> Deref for String<N> {
352  type Target = str;
353  fn deref(&self) -> &str {
354    self.as_str()
355  }
356}
357
358impl<const N: usize> DerefMut for String<N> {
359  fn deref_mut(&mut self) -> &mut str {
360    self.as_mut()
361  }
362}
363
364impl<const N: usize> AsRef<str> for String<N> {
365  fn as_ref(&self) -> &str {
366    self.0.as_str()
367  }
368}
369
370impl<const N: usize> AsMut<str> for String<N> {
371  fn as_mut(&mut self) -> &mut str {
372    core::str::from_utf8_mut(self.0.as_mut_slice()).unwrap()
373  }
374}
375
376impl<const N: usize> AsRef<[u8]> for String<N> {
377  fn as_ref(&self) -> &[u8] {
378    self.0.as_str().as_bytes()
379  }
380}
381
382impl<const N: usize> PartialEq<&str> for String<N> {
383  fn eq(&self, other: &&str) -> bool {
384    self.as_str() == *other
385  }
386}
387
388impl<const N: usize> PartialEq<str> for String<N> {
389  fn eq(&self, other: &str) -> bool {
390    self.as_str() == other
391  }
392}
393
394impl<const N: usize> PartialEq<String<N>> for &str {
395  fn eq(&self, other: &String<N>) -> bool {
396    *self == other.as_str()
397  }
398}
399
400impl<const N: usize> PartialEq<&String<N>> for &str {
401  fn eq(&self, other: &&String<N>) -> bool {
402    *self == other.as_str()
403  }
404}