Skip to main content

textos/unicode/egc/
non_nul.rs

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