devela/text/grapheme/
nonul.rs

1// devela::text::grapheme::nonul
2//
3//!
4//
5// TOC
6// - definitions
7// - trait impls
8// - conversions
9
10use super::Grapheme;
11#[cfg(_char··)]
12use crate::text::char::*;
13#[cfg(feature = "alloc")]
14use crate::CString;
15use crate::{unwrap, ConstDefault, IterChars, MismatchedCapacity, StringNonul};
16// use unicode_segmentation::UnicodeSegmentation;
17
18/* definitions */
19
20/// An <abbr title="Extended Grapheme Cluster">EGC</abbr> backed by a [`StringNonul`].
21#[must_use]
22#[repr(transparent)]
23#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
24pub struct GraphemeNonul<const CAP: usize>(StringNonul<CAP>);
25
26/* impls */
27
28impl<const CAP: usize> GraphemeNonul<CAP> {
29    /// Creates a new empty `GraphemeNonul`.
30    ///
31    /// # Errors
32    /// Returns [`MismatchedCapacity`] if `CAP` > 255.
33    pub const fn new() -> Result<Self, MismatchedCapacity> {
34        Ok(Self(unwrap![ok? StringNonul::new()]))
35    }
36
37    /// Creates a new `GraphemeNonul` from a `char7`.
38    ///
39    /// If `c`.[`is_nul()`][char7#method.is_nul] an empty grapheme will be returned.
40    ///
41    /// # Errors
42    /// Returns [`MismatchedCapacity`] if `CAP` > 255,
43    /// or if `!c.is_nul()` and `CAP` < 1.
44    ///
45    /// Will always succeed if `CAP` >= 1.
46    #[cfg(feature = "_char7")]
47    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "_char7")))]
48    pub const fn from_char7(c: char7) -> Result<Self, MismatchedCapacity> {
49        Ok(Self(unwrap![ok? StringNonul::from_char7(c)]))
50    }
51
52    /// Creates a new `GraphemeNonul` from a `char8`.
53    ///
54    /// If `c`.[`is_nul()`][char8#method.is_nul] an empty grapheme will be returned.
55    ///
56    /// # Errors
57    /// Returns [`MismatchedCapacity`] if `CAP` > 255,
58    /// or if `!c.is_nul()` and `CAP` < `c.`[`len_utf8()`][char8#method.len_utf8].
59    ///
60    /// Will always succeed if `CAP` >= 2.
61    #[cfg(feature = "_char8")]
62    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "_char8")))]
63    pub const fn from_char8(c: char8) -> Result<Self, MismatchedCapacity> {
64        Ok(Self(unwrap![ok? StringNonul::from_char8(c)]))
65    }
66
67    /// Creates a new `GraphemeNonul` from a `char16`.
68    ///
69    /// If `c`.[`is_nul()`][char16#method.is_nul] an empty grapheme will be returned.
70    ///
71    /// # Errors
72    /// Returns [`MismatchedCapacity`] if `CAP` > 255,
73    /// or if `!c.is_nul()` and `CAP` < `c.`[`len_utf8()`][char16#method.len_utf8].
74    ///
75    /// Will always succeed if `CAP` >= 3.
76    #[cfg(feature = "_char16")]
77    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "_char16")))]
78    pub const fn from_char16(c: char16) -> Result<Self, MismatchedCapacity> {
79        Ok(Self(unwrap![ok? StringNonul::from_char16(c)]))
80    }
81
82    /// Creates a new `GraphemeNonul` from a `char`.
83    ///
84    /// If `c`.[`is_nul()`][UnicodeScalar#method.is_nul] an empty grapheme will be returned.
85    ///
86    /// # Errors
87    /// Returns [`MismatchedCapacity`] if `CAP` > 255,
88    /// or if `!c.is_nul()` and `CAP` < `c.`[`len_utf8()`][UnicodeScalar#method.len_utf8].
89    ///
90    /// Will always succeed if `CAP` >= 4.
91    pub const fn from_char(c: char) -> Result<Self, MismatchedCapacity> {
92        Ok(Self(unwrap![ok? StringNonul::from_char(c)]))
93    }
94
95    //
96
97    /// Returns the length in bytes.
98    #[must_use] #[rustfmt::skip]
99    pub const fn len(&self) -> usize { self.0.len() }
100
101    /// Returns `true` if the current length is 0.
102    #[must_use] #[rustfmt::skip]
103    pub const fn is_empty(&self) -> bool { self.0.len() == 0 }
104
105    /// Returns the total capacity in bytes.
106    #[must_use] #[rustfmt::skip]
107    pub const fn capacity() -> usize { CAP }
108
109    /// Returns the remaining capacity.
110    #[must_use] #[rustfmt::skip]
111    pub const fn remaining_capacity(&self) -> usize { CAP - self.len() }
112
113    /// Returns `true` if the current remaining capacity is 0.
114    #[must_use] #[rustfmt::skip]
115    pub const fn is_full(&self) -> bool { self.len() == CAP }
116
117    /// Sets the length to 0, by resetting all bytes to 0.
118    #[rustfmt::skip]
119    pub fn clear(&mut self) { self.0.clear(); }
120
121    //
122
123    /// Returns a byte slice of the inner string slice.
124    #[must_use] #[rustfmt::skip]
125    pub const fn as_bytes(&self) -> &[u8] { self.0.as_bytes() }
126
127    /// Returns a mutable byte slice of the inner string slice.
128    /// # Safety
129    /// The content must be valid UTF-8.
130    #[cfg(all(not(feature = "safe_text"), feature = "unsafe_slice"))]
131    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "unsafe_slice")))]
132    pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
133        // SAFETY: unsafe fn
134        unsafe { self.0.as_bytes_mut() }
135    }
136
137    /// Returns a copy of the inner array with the full contents.
138    ///
139    /// The array contains all the bytes, including those outside the current length.
140    #[must_use] #[rustfmt::skip]
141    pub const fn as_array(&self) -> [u8; CAP] { self.0.as_array() }
142
143    /// Returns the inner array with the full contents.
144    ///
145    /// The array contains all the bytes, including those outside the current length.
146    #[must_use] #[rustfmt::skip]
147    pub const fn into_array(self) -> [u8; CAP] { self.0.into_array() }
148
149    /// Returns the inner string slice.
150    #[must_use] #[rustfmt::skip]
151    pub const fn as_str(&self) -> &str { self.0.as_str() }
152
153    /// Returns the mutable inner string slice.
154    /// # Safety
155    /// The content must be valid UTF-8.
156    #[must_use] #[rustfmt::skip]
157    #[cfg(all(not(feature = "safe_text"), feature = "unsafe_slice"))]
158    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "unsafe_slice")))]
159    pub unsafe fn as_mut_str(&mut self) -> &mut str {
160        // SAFETY: caller must ensure safety
161        unsafe { self.0.as_mut_str() }
162    }
163
164    /// Returns an iterator over the `chars` of this grapheme cluster.
165    #[rustfmt::skip]
166    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "alloc")))]
167    pub fn chars(&self) -> IterChars { self.0.chars() }
168
169    /// Returns a new allocated C-compatible, nul-terminanted string.
170    #[must_use] #[rustfmt::skip]
171    #[cfg(feature = "alloc")]
172    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "alloc")))]
173    pub fn to_cstring(&self) -> CString { self.0.to_cstring() }
174}
175
176/* traits */
177
178impl<const CAP: usize> Grapheme for GraphemeNonul<CAP> {}
179
180mod core_impls {
181    use super::*;
182    use core::fmt;
183
184    impl<const CAP: usize> Default for GraphemeNonul<CAP> {
185        /// Returns an empty extended grapheme character.
186        #[rustfmt::skip]
187        fn default() -> Self { Self::new().unwrap() }
188    }
189    impl<const CAP: usize> ConstDefault for GraphemeNonul<CAP> {
190        /// Returns an empty string.
191        ///
192        /// # Panics
193        /// Panics if `CAP > 255`.
194        const DEFAULT: Self = unwrap![ok Self::new()];
195    }
196
197    impl<const CAP: usize> fmt::Display for GraphemeNonul<CAP> {
198        #[rustfmt::skip]
199        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }
200    }
201    impl<const CAP: usize> fmt::Debug for GraphemeNonul<CAP> {
202        #[rustfmt::skip]
203        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.0) }
204    }
205
206    // TODO
207    // impl<const CAP: usize> From<String> for GraphemeNonul<CAP> {
208    //     fn from(s: String) -> GraphemeNonul<CAP> {
209    //         GraphemeNonul(s.graphemes(true).take(1).collect())
210    //     }
211    // }
212    // impl From<&str> for GraphemeNonul {
213    //     fn from(s: &str) -> GraphemeNonul {
214    //         GraphemeNonul(s.graphemes(true).take(1).collect())
215    //     }
216    // }
217    // impl From<char> for GraphemeNonul {
218    //     fn from(s: char) -> GraphemeNonul {
219    //         GraphemeNonul(s.into())
220    //     }
221    // }
222}