1#[cfg(feature = "alloc")]
12use alloc::{ffi::CString, str::Chars, string::ToString};
13
14use crate::{
15 error::{TextosError as Error, TextosResult as Result},
16 macros::impl_sized_alias,
17 unicode::char::*,
18};
19use core::{fmt, ops::Deref};
20use devela::codegen::paste;
21
22#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
29pub struct StaticU8String<const CAP: usize> {
30 arr: [u8; CAP],
32 len: u8,
33}
34
35impl_sized_alias![
36 String, StaticU8String,
37 "UTF-8-encoded string, with fixed capacity of ", ".":
38 "A" 16, 1 "";
39 "A" 24, 2 "s";
40 "A" 32, 3 "s";
41 "A" 40, 4 "s";
42 "A" 48, 5 "s";
43 "A" 56, 6 "s";
44 "A" 64, 7 "s";
45 "A" 128, 15 "s";
46 "A" 256, 31 "s";
47 "A" 512, 63 "s";
48 "A" 1024, 127 "s";
49 "A" 2048, 255 "s"
50];
51
52impl<const CAP: usize> StaticU8String<CAP> {
53 #[inline]
58 pub const fn new() -> Self {
59 assert![CAP <= 255];
60 Self {
61 arr: [0; CAP],
62 len: 0,
63 }
64 }
65
66 #[inline]
73 pub const fn from_char7(c: Char7) -> Self {
74 let mut new = Self::new();
75 new.arr[0] = c.to_utf8_bytes()[0];
76 new.len = 1;
77 new
78 }
79
80 #[inline]
87 pub const fn from_char8(c: Char8) -> Self {
88 let mut new = Self::new();
89
90 let bytes = c.to_utf8_bytes();
91 new.len = char_utf8_2bytes_len(bytes);
92
93 new.arr[0] = bytes[0];
94 if new.len > 1 {
95 new.arr[1] = bytes[1];
96 }
97 new
98 }
99
100 #[inline]
107 pub const fn from_char16(c: Char16) -> Self {
108 let mut new = Self::new();
109
110 let bytes = c.to_utf8_bytes();
111 new.len = char_utf8_3bytes_len(bytes);
112
113 new.arr[0] = bytes[0];
114 if new.len > 1 {
115 new.arr[1] = bytes[1];
116 }
117 if new.len > 2 {
118 new.arr[2] = bytes[2];
119 }
120 new
121 }
122
123 #[inline]
130 pub const fn from_char24(c: Char24) -> Self {
131 let mut new = Self::new();
132
133 let bytes = c.to_utf8_bytes();
134 new.len = char_utf8_4bytes_len(bytes);
135
136 new.arr[0] = bytes[0];
137 if new.len > 1 {
138 new.arr[1] = bytes[1];
139 }
140 if new.len > 2 {
141 new.arr[2] = bytes[2];
142 }
143 if new.len > 3 {
144 new.arr[3] = bytes[3];
145 }
146 new
147 }
148
149 #[inline]
156 pub const fn from_char32(c: Char32) -> Self {
157 let mut new = Self::new();
158
159 let bytes = c.to_utf8_bytes();
160 new.len = char_utf8_4bytes_len(bytes);
161
162 new.arr[0] = bytes[0];
163 if new.len > 1 {
164 new.arr[1] = bytes[1];
165 }
166 if new.len > 2 {
167 new.arr[2] = bytes[2];
168 }
169 if new.len > 3 {
170 new.arr[3] = bytes[3];
171 }
172 new
173 }
174
175 #[inline]
182 pub const fn from_char(c: char) -> Self {
183 Self::from_char32(Char32(c))
184 }
185
186 #[inline]
190 pub const fn capacity() -> usize {
191 CAP
192 }
193
194 #[inline]
196 pub const fn remaining_capacity(&self) -> usize {
197 CAP - self.len as usize
198 }
199
200 #[inline]
202 pub const fn len(&self) -> usize {
203 self.len as usize
204 }
205
206 #[inline]
208 pub const fn is_empty(&self) -> bool {
209 self.len == 0
210 }
211
212 #[inline]
214 pub const fn is_full(&self) -> bool {
215 self.len == CAP as u8
216 }
217
218 #[inline]
220 pub fn clear(&mut self) {
221 self.len = 0;
222 }
223
224 #[inline]
226 pub fn reset(&mut self) {
227 self.arr = [0; CAP];
228 self.len = 0;
229 }
230
231 #[inline]
235 pub fn as_bytes(&self) -> &[u8] {
236 #[cfg(feature = "unsafe")]
237 unsafe {
238 self.arr.get_unchecked(0..self.len as usize)
239 }
240
241 #[cfg(not(feature = "unsafe"))]
242 self.arr
243 .get(0..self.len as usize)
244 .expect("len must be <= arr.len()")
245 }
246
247 #[inline]
249 #[cfg(feature = "unsafe")]
250 #[cfg_attr(feature = "nightly", doc(cfg(feature = "unsafe")))]
251 pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
252 self.arr.get_unchecked_mut(0..self.len as usize)
253 }
254
255 #[inline]
259 pub const fn as_array(&self) -> [u8; CAP] {
260 self.arr
261 }
262
263 #[inline]
267 pub const fn into_array(self) -> [u8; CAP] {
268 self.arr
269 }
270
271 pub fn as_str(&self) -> &str {
273 #[cfg(feature = "unsafe")]
274 unsafe {
275 core::str::from_utf8_unchecked(
276 self.arr
277 .get(0..self.len as usize)
278 .expect("len must be <= arr.len()"),
279 )
280 }
281 #[cfg(not(feature = "unsafe"))]
282 core::str::from_utf8(
283 self.arr
284 .get(0..self.len as usize)
285 .expect("len must be <= arr.len()"),
286 )
287 .expect("must be valid utf-8")
288 }
289
290 #[cfg(feature = "unsafe")]
292 #[cfg_attr(feature = "nightly", doc(cfg(feature = "unsafe")))]
293 pub fn as_str_mut(&mut self) -> &mut str {
294 unsafe { &mut *(self.as_bytes_mut() as *mut [u8] as *mut str) }
295 }
296
297 #[cfg(feature = "alloc")]
299 #[cfg_attr(feature = "nightly", doc(cfg(feature = "alloc")))]
300 pub fn chars(&self) -> Chars {
301 self.as_str().chars()
302 }
303
304 #[inline]
306 #[cfg(feature = "alloc")]
307 #[cfg_attr(feature = "nightly", doc(cfg(feature = "alloc")))]
308 pub fn to_cstring(&self) -> CString {
309 CString::new(self.to_string()).unwrap()
310 }
311
312 #[inline]
317 pub fn pop(&mut self) -> Option<char> {
318 self.as_str().chars().last().map(|c| {
319 self.len -= c.len_utf8() as u8;
320 c
321 })
322 }
323
324 #[inline]
330 pub fn try_pop(&mut self) -> Result<char> {
331 self.as_str()
332 .chars()
333 .last()
334 .map(|c| {
335 self.len -= c.len_utf8() as u8;
336 c
337 })
338 .ok_or(Error::NotEnoughElements(1))
339 }
340
341 pub fn push(&mut self, character: char) -> usize {
348 let char_len = character.len_utf8();
349 if self.remaining_capacity() >= char_len {
350 let beg = self.len as usize;
351 let end = beg + char_len;
352 let _ = character.encode_utf8(&mut self.arr[beg..end]);
353 self.len += char_len as u8;
354 char_len
355 } else {
356 0
357 }
358 }
359
360 pub fn try_push(&mut self, character: char) -> Result<usize> {
367 let char_len = character.len_utf8();
368 if self.remaining_capacity() >= char_len {
369 let beg = self.len as usize;
370 let end = beg + char_len;
371 let _ = character.encode_utf8(&mut self.arr[beg..end]);
372 self.len += char_len as u8;
373 Ok(char_len)
374 } else {
375 Err(Error::NotEnoughCapacity(char_len))
376 }
377 }
378}
379
380impl<const CAP: usize> Default for StaticU8String<CAP> {
383 #[inline]
388 fn default() -> Self {
389 Self::new()
390 }
391}
392
393impl<const CAP: usize> fmt::Display for StaticU8String<CAP> {
394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 write!(f, "{}", self.as_str())
396 }
397}
398
399impl<const CAP: usize> fmt::Debug for StaticU8String<CAP> {
400 #[inline]
401 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402 write!(f, "{:?}", self.as_str())
403 }
404}
405
406impl<const CAP: usize> Deref for StaticU8String<CAP> {
407 type Target = str;
408 fn deref(&self) -> &Self::Target {
409 self.as_str()
410 }
411}
412
413macro_rules! impl_from_char {
416 ( $char:ty => $for_name:ident: $( $for_bit:expr ),+ ) => {
420 $( impl_from_char![@$char => $for_name: $for_bit]; )+
421 };
422 ( @$char:ty => $for_name:ident: $for_bit:expr ) => { paste! {
423 impl From<$char> for [< $for_name $for_bit >] {
424 fn from(c: $char) -> [< $for_name $for_bit >] {
425 let mut s = Self::default();
426 let _ = s.push(c.into());
427 s
428 }
429 }
430 }};
431 ( try $char:ty => $for_name:ident: $( $for_bit:expr ),+ ) => {
432 $( impl_from_char![@try $char => $for_name: $for_bit]; )+
433 };
434 ( @try $char:ty => $for_name:ident: $for_bit:expr ) => { paste! {
435 impl TryFrom<$char> for [< $for_name $for_bit >] {
436 type Error = Error;
437 fn try_from(c: $char) -> Result<[< $for_name $for_bit >]> {
438 let mut s = Self::default();
439 s.try_push(c.into())?;
440 Ok(s)
441 }
442 }
443 }};
444}
445impl_from_char![Char7 => String: 16, 24, 32, 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
446impl_from_char![Char8 => String: 24, 32, 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
447impl_from_char![try Char8 => String: 16];
448impl_from_char![Char16 => String: 32, 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
449impl_from_char![try Char16 => String: 16, 24];
450impl_from_char![Char24 => String: 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
451impl_from_char![try Char24 => String: 16, 24, 32];
452impl_from_char![Char32 => String: 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
453impl_from_char![try Char32 => String: 16, 24, 32];
454impl_from_char![char => String: 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
455impl_from_char![try char => String: 16, 24, 32];
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460
461 #[test]
462 fn push() {
463 let mut s = String32::new(); assert![s.try_push('ñ').is_ok()];
466 assert_eq![2, s.len()];
467 assert![s.try_push('ñ').is_err()];
468 assert_eq![2, s.len()];
469 assert![s.try_push('a').is_ok()];
470 assert_eq![3, s.len()];
471 }
472
473 #[test]
475 fn pop() {
476 let mut s = String32::new(); s.push('ñ');
479 s.push('a');
480 assert_eq![Some('a'), s.pop()];
481 assert_eq![Some('ñ'), s.pop()];
482 assert_eq![None, s.pop()];
483 }
484}