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}