textos/unicode/egc/
u8string.rs

1// textos::unicode::egc::u8string
2//
3//!
4//
5// TOC
6// - definitions
7// - trait impls
8// - conversions
9
10use crate::{
11    error::{TextosError as Error, TextosResult as Result},
12    macros::impl_sized_alias,
13    unicode::{
14        char::*,
15        egc::Egcs,
16        string::{StaticU8String, Strings},
17    },
18};
19#[cfg(feature = "alloc")]
20use alloc::{ffi::CString, str::Chars};
21use core::fmt;
22use devela::codegen::paste;
23// use unicode_segmentation::UnicodeSegmentation;
24
25/* definitions */
26
27/// An <abbr title="Extended Grapheme Cluster">EGC</abbr> backed by a
28/// [`StaticU8String`].
29#[derive(Clone, PartialEq, Eq, Hash)]
30#[repr(transparent)]
31pub struct StaticU8Egc<const CAP: usize>(StaticU8String<CAP>);
32
33impl_sized_alias![
34    Egc, StaticU8Egc,
35    "<abbr title='Extended Grapheme Cluster'>EGC</abbr>, with fixed capacity of ",
36    ".":
37    "A" 16, 1 "";
38    "A" 24, 2 "s";
39    "A" 32, 3 "s";
40    "A" 40, 4 "s";
41    "A" 48, 5 "s";
42    "A" 56, 6 "s";
43    "A" 64, 7 "s";
44    "A" 128, 15 "s"
45];
46
47impl<const CAP: usize> StaticU8Egc<CAP> {
48    /// Creates a new empty `StaticU8Egc`.
49    ///
50    /// # Panics
51    /// Panics if `CAP` > 255.
52    #[inline]
53    #[must_use]
54    pub const fn new() -> Self {
55        Self(StaticU8String::new())
56    }
57
58    /// Creates a new `StaticU8Egc` from a `Char7`.
59    ///
60    /// # Panic
61    /// Panics if `CAP` > 255 or < 1.
62    ///
63    /// Will never panic if `CAP` >= 1 and <= 255.
64    #[inline]
65    #[must_use]
66    pub const fn from_char7(c: Char7) -> Self {
67        Self(StaticU8String::from_char7(c))
68    }
69
70    /// Creates a new `StaticU8Egc` from a `Char8`.
71    ///
72    /// # Panic
73    /// Panics if `CAP` > 255 or < `c.`[`len_utf8()`][Char8#method.len_utf8].
74    ///
75    /// Will never panic if `CAP` >= 2 and <= 255.
76    #[inline]
77    #[must_use]
78    pub const fn from_char8(c: Char8) -> Self {
79        Self(StaticU8String::from_char8(c))
80    }
81
82    /// Creates a new `StaticU8Egc` from a `Char16`.
83    ///
84    /// # Panic
85    /// Panics if `CAP` > 255 or < `c.`[`len_utf8()`][Char16#method.len_utf8].
86    ///
87    /// Will never panic if `CAP` >= 3 and <= 255.
88    #[inline]
89    #[must_use]
90    pub const fn from_char16(c: Char16) -> Self {
91        Self(StaticU8String::from_char16(c))
92    }
93
94    /// Creates a new `StaticU8Egc` from a `Char24`.
95    ///
96    /// # Panic
97    /// Panics if `CAP` > 255 or < `c.`[`len_utf8()`][Char24#method.len_utf8].
98    ///
99    /// Will never panic if `CAP` >= 4 and <= 255.
100    #[inline]
101    #[must_use]
102    pub const fn from_char24(c: Char24) -> Self {
103        Self(StaticU8String::from_char24(c))
104    }
105
106    /// Creates a new `StaticU8Egc` from a `Char32`.
107    ///
108    /// # Panic
109    /// Panics if `CAP` > 255 or < `c.`[`len_utf8()`][Char32#method.len_utf8].
110    ///
111    /// Will never panic if `CAP` >= 4 and <= 255.
112    #[inline]
113    #[must_use]
114    pub const fn from_char32(c: Char32) -> Self {
115        Self(StaticU8String::from_char32(c))
116    }
117
118    /// Creates a new `StaticU8Egc` from a `char`.
119    ///
120    /// # Panic
121    /// Panics if `CAP` > 255 or < `c.`[`len_utf8()`][Chars#method.len_utf8].
122    ///
123    /// Will never panic if `CAP` >= 4 and <= 255.
124    #[inline]
125    #[must_use]
126    pub const fn from_char(c: char) -> Self {
127        Self::from_char32(Char32(c))
128    }
129
130    //
131
132    /// Returns the length in bytes.
133    pub const fn len(&self) -> usize {
134        self.0.len()
135    }
136
137    /// Returns `true` if the current length is 0.
138    pub const fn is_empty(&self) -> bool {
139        self.0.len() == 0
140    }
141
142    /// Returns the total capacity in bytes.
143    #[inline]
144    pub const fn capacity() -> usize {
145        CAP
146    }
147
148    /// Returns the remaining capacity.
149    #[inline]
150    pub const fn remaining_capacity(&self) -> usize {
151        CAP - self.len()
152    }
153
154    /// Returns `true` if the current remaining capacity is 0.
155    #[inline]
156    pub const fn is_full(&self) -> bool {
157        self.len() == CAP
158    }
159
160    /// Sets the length to 0, by resetting all bytes to 0.
161    #[inline]
162    pub fn clear(&mut self) {
163        self.0.clear();
164    }
165
166    //
167
168    /// Returns a byte slice of the inner string slice.
169    #[inline]
170    pub fn as_bytes(&self) -> &[u8] {
171        self.0.as_bytes()
172    }
173
174    /// Returns a mutable byte slice of the inner string slice.
175    #[inline]
176    #[cfg(feature = "unsafe")]
177    #[cfg_attr(feature = "nightly", doc(cfg(feature = "unsafe")))]
178    pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
179        self.0.as_bytes_mut()
180    }
181
182    /// Returns a copy of the inner array with the full contents.
183    ///
184    /// The array contains all the bytes, including those outside the current length.
185    #[inline]
186    pub const fn as_array(&self) -> [u8; CAP] {
187        self.0.as_array()
188    }
189
190    /// Returns the inner array with the full contents.
191    ///
192    /// The array contains all the bytes, including those outside the current length.
193    #[inline]
194    pub const fn into_array(self) -> [u8; CAP] {
195        self.0.into_array()
196    }
197
198    /// Returns the inner string slice.
199    #[inline]
200    pub fn as_str(&self) -> &str {
201        self.0.as_str()
202    }
203
204    /// Returns the mutable inner string slice.
205    #[cfg(feature = "unsafe")]
206    #[cfg_attr(feature = "nightly", doc(cfg(feature = "unsafe")))]
207    pub unsafe fn as_str_mut(&mut self) -> &mut str {
208        self.0.as_str_mut()
209    }
210
211    /// Returns an iterator over the `chars` of this grapheme cluster.
212    #[cfg(feature = "alloc")]
213    #[cfg_attr(feature = "nightly", doc(cfg(feature = "alloc")))]
214    pub fn chars(&self) -> Chars {
215        self.0.chars()
216    }
217
218    /// Returns a new allocated C-compatible, nul-terminanted string.
219    #[inline]
220    #[cfg(feature = "alloc")]
221    #[cfg_attr(feature = "nightly", doc(cfg(feature = "alloc")))]
222    pub fn to_cstring(&self) -> CString {
223        self.0.to_cstring()
224    }
225}
226
227/* traits */
228
229impl<const CAP: usize> Strings for StaticU8Egc<CAP> {}
230impl<const CAP: usize> Egcs for StaticU8Egc<CAP> {}
231
232mod core_impls {
233    use super::*;
234
235    impl<const CAP: usize> Default for StaticU8Egc<CAP> {
236        /// Returns an empty extended grapheme character.
237        #[inline]
238        fn default() -> Self {
239            Self::new()
240        }
241    }
242
243    impl<const CAP: usize> fmt::Display for StaticU8Egc<CAP> {
244        #[inline]
245        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246            write!(f, "{}", self.0)
247        }
248    }
249    impl<const CAP: usize> fmt::Debug for StaticU8Egc<CAP> {
250        #[inline]
251        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252            write!(f, "{:?}", self.0)
253        }
254    }
255
256    // impl From<String> for StaticU8Egc {
257    //     fn from(s: String) -> StaticU8Egc {
258    //         StaticU8Egc(s.graphemes(true).take(1).collect())
259    //     }
260    // }
261    // impl From<&str> for StaticU8Egc {
262    //     fn from(s: &str) -> StaticU8Egc {
263    //         StaticU8Egc(s.graphemes(true).take(1).collect())
264    //     }
265    // }
266    // impl From<char> for StaticU8Egc {
267    //     fn from(s: char) -> StaticU8Egc {
268    //         StaticU8Egc(s.into())
269    //     }
270    // }
271}
272
273macro_rules! impl_from_char {
274    // $char:ty char type
275    // $for_name: `for` type name prefix
276    // $bit: size in bits.
277    ( $char:ty => $for_name:ident: $( $for_bit:expr ),+ ) => {
278        $( impl_from_char![@$char => $for_name: $for_bit]; )+
279    };
280    ( @$char:ty => $for_name:ident: $for_bit:expr ) => { paste! {
281        impl From<$char> for [< $for_name $for_bit >] {
282            fn from(c: $char) -> [< $for_name $for_bit >] {
283                let mut s = Self::default();
284                let _ = s.0.push(c.into());
285                s
286            }
287        }
288    }};
289    ( try $char:ty => $for_name:ident: $( $for_bit:expr ),+ ) => {
290        $( impl_from_char![@try $char => $for_name: $for_bit]; )+
291    };
292    ( @try $char:ty => $for_name:ident: $for_bit:expr ) => { paste! {
293        impl TryFrom<$char> for [< $for_name $for_bit >] {
294            type Error = Error;
295            fn try_from(c: $char) -> Result<[< $for_name $for_bit >]> {
296                let mut s = Self::default();
297                s.0.try_push(c.into())?;
298                Ok(s)
299            }
300        }
301    }};
302}
303impl_from_char![Char7 => Egc: 16, 24, 32, 40, 48, 56, 64, 128];
304impl_from_char![Char8 => Egc: 24, 32, 40, 48, 56, 64, 128];
305impl_from_char![try Char8 => Egc: 16];
306impl_from_char![Char16 => Egc: 32, 40, 48, 56, 64, 128];
307impl_from_char![try Char16 => Egc: 16, 24];
308impl_from_char![Char24 => Egc: 40, 48, 56, 64, 128];
309impl_from_char![try Char24 => Egc: 16, 24, 32];
310impl_from_char![Char32 => Egc: 40, 48, 56, 64, 128];
311impl_from_char![try Char32 => Egc: 16, 24, 32];
312impl_from_char![char => Egc: 40, 48, 56, 64, 128];
313impl_from_char![try char => Egc: 16, 24, 32];