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}