smart_string/pascal_string/
mod.rs1use std::borrow::Borrow;
2use std::borrow::Cow;
3use std::cmp;
4use std::fmt;
5use std::hash::Hash;
6use std::hash::Hasher;
7use std::ops;
8use std::rc::Rc;
9use std::str::from_utf8_unchecked;
10use std::str::from_utf8_unchecked_mut;
11use std::sync::Arc;
12
13use crate::DisplayExt;
14
15mod error;
16#[cfg(feature = "serde")]
17mod with_serde;
18
19pub use error::TryFromBytesError;
20pub use error::TryFromStrError;
21
22#[derive(Clone, Copy)]
23#[repr(C)]
24pub struct PascalString<const CAPACITY: usize> {
25 len: u8,
26 data: [u8; CAPACITY],
27}
28
29impl<const CAPACITY: usize> PascalString<CAPACITY> {
30 pub const CAPACITY: usize = {
31 assert!(
32 CAPACITY <= u8::MAX as usize,
33 "PascalString max capacity is 255"
34 );
35 CAPACITY
36 };
37
38 #[inline(always)]
39 pub const fn new() -> Self {
40 let _ = Self::CAPACITY;
43
44 Self {
45 len: 0,
46 data: [0; CAPACITY],
47 }
48 }
49
50 #[inline]
55 pub fn from_str_truncated(string: &str) -> Self {
56 let _ = Self::CAPACITY;
57
58 if let Ok(ps) = Self::try_from(string) {
59 return ps;
60 }
61
62 let mut ps = Self::new();
63 ps.push_str_truncated(string);
64 ps
65 }
66
67 #[inline(always)]
68 pub fn into_inner(self) -> (u8, [u8; CAPACITY]) {
69 (self.len, self.data)
70 }
71
72 #[inline(always)]
73 pub const fn capacity(&self) -> usize {
74 CAPACITY
75 }
76
77 #[inline(always)]
78 pub fn len(&self) -> usize {
79 self.len as usize
80 }
81
82 #[inline(always)]
83 pub fn is_empty(&self) -> bool {
84 self.len == 0
85 }
86
87 #[inline(always)]
88 pub fn as_str(&self) -> &str {
89 self
90 }
91
92 #[inline(always)]
93 pub fn as_str_mut(&mut self) -> &str {
94 self
95 }
96
97 #[inline]
98 pub fn try_push_str(&mut self, string: &str) -> Result<(), TryFromStrError> {
99 let len = self.len();
100 let new_len = len + string.len();
101
102 if new_len > CAPACITY {
103 return Err(TryFromStrError::TooLong);
104 }
105
106 self.data[len..new_len].copy_from_slice(string.as_bytes());
107 self.len = new_len as u8;
108
109 Ok(())
110 }
111
112 #[inline]
113 pub fn try_push(&mut self, ch: char) -> Result<(), TryFromStrError> {
114 self.try_push_str(ch.encode_utf8(&mut [0; 4]))
116 }
117
118 #[inline]
120 pub fn push_str_truncated<'s>(&mut self, string: &'s str) -> &'s str {
121 if self.try_push_str(string).is_ok() {
122 return "";
123 }
124
125 let mut new_len = self.len();
129 for c in string.chars() {
130 let len = c.len_utf8();
131 if new_len + len > CAPACITY {
132 break;
133 };
134 new_len += len;
135 }
136
137 let pos = new_len - self.len();
138 let (substring, remainder) = string.split_at(pos);
139 self.try_push_str(substring).unwrap();
140
141 remainder
142 }
143
144 #[inline]
145 pub fn truncate(&mut self, new_len: usize) {
146 if new_len <= self.len() {
147 assert!(self.is_char_boundary(new_len));
148 self.len = new_len as u8;
149 }
150 }
151
152 #[inline]
153 pub fn pop(&mut self) -> Option<char> {
154 let ch = self.chars().rev().next()?;
155 let newlen = self.len() - ch.len_utf8();
156 self.len = newlen as u8;
157 Some(ch)
158 }
159
160 #[inline]
161 pub fn clear(&mut self) {
162 self.len = 0;
163 }
164}
165
166impl<const CAPACITY: usize> Default for PascalString<CAPACITY> {
169 #[inline(always)]
170 fn default() -> Self {
171 let _ = Self::CAPACITY;
172
173 Self::new()
174 }
175}
176
177impl<T: ops::Deref<Target = str> + ?Sized, const CAPACITY: usize> PartialEq<T>
178 for PascalString<CAPACITY>
179{
180 #[inline(always)]
181 fn eq(&self, other: &T) -> bool {
182 self.as_str().eq(other.deref())
183 }
184}
185
186macro_rules! impl_reverse_eq_for_str_types {
187 ($($t:ty),*) => {
188 $(
189 impl<const CAPACITY: usize> PartialEq<PascalString<CAPACITY>> for $t {
190 #[inline(always)]
191 fn eq(&self, other: &PascalString<CAPACITY>) -> bool {
192 let a: &str = self.as_ref();
193 let b = other.as_str();
194 a.eq(b)
195 }
196 }
197
198 impl<const CAPACITY: usize> PartialEq<PascalString<CAPACITY>> for &$t {
199 #[inline(always)]
200 fn eq(&self, other: &PascalString<CAPACITY>) -> bool {
201 let a: &str = self.as_ref();
202 let b = other.as_str();
203 a.eq(b)
204 }
205 }
206
207 impl<const CAPACITY: usize> PartialEq<PascalString<CAPACITY>> for &mut $t {
208 #[inline(always)]
209 fn eq(&self, other: &PascalString<CAPACITY>) -> bool {
210 let a: &str = self.as_ref();
211 let b = other.as_str();
212 a.eq(b)
213 }
214 }
215 )*
216 };
217}
218
219impl_reverse_eq_for_str_types!(String, str, Cow<'_, str>, Box<str>, Rc<str>, Arc<str>);
220
221impl<const CAPACITY: usize> Eq for PascalString<CAPACITY> {}
222
223impl<T: ops::Deref<Target = str>, const CAPACITY: usize> PartialOrd<T> for PascalString<CAPACITY> {
224 #[inline(always)]
225 fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
226 self.as_str().partial_cmp(other.deref())
227 }
228}
229
230impl<const CAPACITY: usize> Ord for PascalString<CAPACITY> {
231 #[inline(always)]
232 fn cmp(&self, other: &Self) -> cmp::Ordering {
233 self.as_str().cmp(other.as_str())
234 }
235}
236
237impl<const CAPACITY: usize> Hash for PascalString<CAPACITY> {
238 #[inline(always)]
239 fn hash<H: Hasher>(&self, state: &mut H) {
240 self.as_str().hash(state)
241 }
242}
243
244impl<const CAPACITY: usize> fmt::Debug for PascalString<CAPACITY> {
247 #[inline(always)]
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 let name: PascalString<39> = format_args!("PascalString<{CAPACITY}>")
250 .try_to_fmt()
251 .unwrap_or_else(|_| "PascalString<?>".to_fmt());
252 f.debug_tuple(&name).field(&self.as_str()).finish()
253 }
254}
255
256impl<const CAPACITY: usize> fmt::Display for PascalString<CAPACITY> {
257 #[inline(always)]
258 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259 self.as_str().fmt(f)
260 }
261}
262
263impl<const CAPACITY: usize> ops::Deref for PascalString<CAPACITY> {
266 type Target = str;
267
268 #[inline(always)]
269 fn deref(&self) -> &Self::Target {
270 let bytes = unsafe { self.data.get_unchecked(..self.len()) };
272 unsafe { from_utf8_unchecked(bytes) }
274 }
275}
276
277impl<const CAPACITY: usize> ops::DerefMut for PascalString<CAPACITY> {
278 #[inline(always)]
279 fn deref_mut(&mut self) -> &mut Self::Target {
280 let len = self.len();
281 let bytes = unsafe { self.data.get_unchecked_mut(..len) };
283 unsafe { from_utf8_unchecked_mut(bytes) }
285 }
286}
287
288impl<const CAPACITY: usize> Borrow<str> for PascalString<CAPACITY> {
289 #[inline(always)]
290 fn borrow(&self) -> &str {
291 self
292 }
293}
294
295impl<const CAPACITY: usize> AsRef<str> for PascalString<CAPACITY> {
296 #[inline(always)]
297 fn as_ref(&self) -> &str {
298 self
299 }
300}
301
302impl<const CAPACITY: usize> AsRef<[u8]> for PascalString<CAPACITY> {
303 #[inline(always)]
304 fn as_ref(&self) -> &[u8] {
305 self.as_bytes()
306 }
307}
308
309impl<'a, const CAPACITY: usize> TryFrom<&'a [u8]> for PascalString<CAPACITY> {
312 type Error = TryFromBytesError;
313
314 #[inline]
315 fn try_from(bytes: &'a [u8]) -> Result<PascalString<CAPACITY>, Self::Error> {
316 let _ = Self::CAPACITY;
317
318 let string = core::str::from_utf8(bytes)?;
319 Ok(Self::try_from(string)?)
320 }
321}
322
323impl<'a, const CAPACITY: usize> TryFrom<&'a mut str> for PascalString<CAPACITY> {
324 type Error = TryFromStrError;
325
326 #[inline]
327 fn try_from(value: &'a mut str) -> Result<PascalString<CAPACITY>, Self::Error> {
328 Self::try_from(&*value)
329 }
330}
331
332impl<'a, const CAPACITY: usize> TryFrom<&'a str> for PascalString<CAPACITY> {
333 type Error = TryFromStrError;
334
335 #[inline]
336 fn try_from(value: &'a str) -> Result<PascalString<CAPACITY>, Self::Error> {
337 let _ = Self::CAPACITY;
338
339 let bytes = value.as_bytes();
340 let len = bytes.len();
341
342 if len > CAPACITY {
343 return Err(TryFromStrError::TooLong);
344 }
345
346 let data = match <[u8; CAPACITY]>::try_from(bytes).ok() {
347 Some(data) => data,
348 None => {
349 let mut data = [0; CAPACITY];
350 data[..len].copy_from_slice(bytes);
351 data
352 }
353 };
354
355 let len = len as u8;
356
357 Ok(PascalString { len, data })
358 }
359}
360
361impl<const CAPACITY: usize> TryFrom<char> for PascalString<CAPACITY> {
362 type Error = TryFromStrError;
363
364 #[inline]
365 fn try_from(value: char) -> Result<PascalString<CAPACITY>, Self::Error> {
366 let _ = Self::CAPACITY;
367
368 Self::try_from(value.encode_utf8(&mut [0; 4]))
369 }
370}
371
372impl<const CAPACITY: usize> fmt::Write for PascalString<CAPACITY> {
375 #[inline]
376 fn write_str(&mut self, s: &str) -> fmt::Result {
377 self.try_push_str(s).map_err(|_| fmt::Error)
378 }
379}
380
381#[cfg(test)]
384mod tests {
385 use std::mem;
386
387 use super::*;
388
389 #[test]
390 fn test_eq() {
391 use std::fmt::Write;
392
393 let s = String::from("abc");
394 let ps = PascalString::<4>::try_from("abc").unwrap();
395
396 assert_eq!(ps, s);
397 let s = String::from("abcd");
401 let mut ps = PascalString::<4>::new();
402 write!(&mut ps, "abcd").unwrap();
403
404 assert_eq!(ps, s);
405 }
406
407 #[test]
408 fn test_ord() {
409 let ps1 = PascalString::<4>::try_from("abc").unwrap();
410 let ps2 = PascalString::<4>::try_from("abcd").unwrap();
411
412 assert!(ps1 < ps2);
413 assert!(ps1 <= ps2);
414 assert!(ps2 > ps1);
415 assert!(ps2 >= ps1);
416 }
417
418 #[test]
419 fn test_size() {
420 assert_eq!(mem::size_of::<PascalString<0>>(), 1);
421 assert_eq!(mem::size_of::<PascalString<1>>(), 2);
422 assert_eq!(mem::size_of::<PascalString<2>>(), 3);
423 assert_eq!(mem::size_of::<PascalString<3>>(), 4);
424 assert_eq!(mem::size_of::<PascalString<4>>(), 5);
425 }
426
427 fn _test_max_size() {
430 let _x: PascalString<256> = PascalString::default();
442 let _x: PascalString<256> = PascalString::new();
443 let _x: PascalString<256> = PascalString::try_from("").unwrap();
444 let _x: PascalString<256> = PascalString::from_str_truncated("");
445 }
446
447 #[test]
448 fn test_deref() {
449 let ps = PascalString::<3>::try_from("abc").unwrap();
450 let map: std::collections::HashSet<_> = ["abc"].into_iter().collect();
451 assert!(map.contains(&*ps));
452 }
453}